javascripttypescriptasynchronousconstructor

Is async function awaited automatically if call in constructor?


I'm working on some code which needs to be changed to be async (because of library upgrade). My code has same base class which is inherited by other classes and I want to call some functions in constructor which are async now. As I know it's impossible to await constructor but what I found async function used inside constructor is awaited. From logical point it shouldn't work but it works. Can someone confirm explain why it works?

Example

abstract class Base {
    constructor() {
        // do some setup
        this.asyncMethod();
    }

    abstract asyncMethod(): Promise<void>;
}

class Test extends Base {
    constructor() {
        super();
    }

    async asyncMethod(): Promise<void> {
        await setTimeout(() => console.log('Tadaaaa'));
    }
}

const instance = new Test();

Solution

  • No it is not. And constructors can't be async.

    A test that shows the constructor isn't waiting

    I simplified the code from the question into an example that clearly shows that the constructor does not wait for an async method. When yo u run this code you will see that you get 'Constructor done' from the constructor before 'All done' that is written from the async method.

    class MyClass {
      constructor() {
        this.asyncMethod();
        console.log('Constructor done');
      }
    
      sleep(ms) {
        return new Promise(res => setTimeout(res, ms));
      }
    
      async asyncMethod() {
        await this.sleep(500);
        console.log('All done');
      }
    }
    
    const instance = new MyClass();

    A workaround if you need something similar to an async constructor

    A workaround would be to make the constructor uncallable and make a static method that is a factory that creates new instances of the class.

    class Dog {
      // make 'new Dog()' throw an error
      constructor() {
        throw new Error('Please call await Dog.new() to create a dog.');
      }
      // a factory method that creates a dog
      static async new(...args) {
        let instance = Object.create(Dog.prototype);
        await instance._constructor(...args);
        return instance;
      }
      // our 'real' constructor
      async _constructor(name) {
        this.name = name;
        /* do async stuff */
      }
    }
    
    // new Dog(); -> would throw an error
    async function test() {
      // instead create dog instances like this
      let lassie = await Dog.new('Lassie');
      console.log(lassie.constructor.name);
      console.log(lassie);
    }
    test();