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();
No it is not. And constructors can't be async.
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 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();