skip to Main Content

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();

2

Answers


  1. 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);
        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);
    }
    test();
    Login or Signup to reply.
  2. Actually… You can make constructors async by simply returning the promise:

    #1 — Technically async

    • Pro: returns a Promise
    • Pro: Easy to write
    • Con: Cannot use await
    class Base {
        // This is technically async--since it returns a Promise
        // But, I'm assuming you meant if you could use `await` within--which is no, see method #2
        constructor() {
            // You can NOT use `await` within the constructor here :(
            return this.asyncMethod();
        }
    
        async asyncMethod() {
            // You CAN use `await` here :)
            return fetch('#');
        }
    }
    

    #2 — Actually async

    • Pro: returns a Promise
    • Pro: can use await
    • Con: Has to be overengineered
    function Base(...args) {
        // This is truly async--it is a promise
        async function Base() {
            // You can use `await` here! :D
            return await this.asyncMethod();
        }
    
        Base.prototype.asyncMethod = function asyncMethod() {
            // You can change this to be `async`
            return fetch('#');
        };
    
        return Base.call(null, args);
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search