DEV Community

Discussion on: The Proper Way to Write Async Constructors in JavaScript

Collapse
 
somedood profile image
Basti Ortiz • Edited

Well, we must first recall that async functions always return Promise instances. Hence, returning an async IIFE from a constructor is the same as if we returned a plain Promise, which is exactly what I argued against for Workaround #1. The main issue is that although this is acceptable according to the ECMAScript Standard (which allows arbitrary values to be returned from a constructor), it is sub-optimal in that it is surprising and even unexpected for most users—even to the TypeScript type inference engine! You can refer to the section about Workaround #1 for more details on why this is not ideal.

Collapse
 
dandv profile image
Dan Dascalescu

I've read the 5 drawbacks of Workaround #1, but I don't quite see how they apply to the code I'm proposing. async-await works, there's no chaining of Promises, yes we use an arrow function to preserve this (is that a big deal?), and the last two points are unclear.

Thread Thread
 
somedood profile image
Basti Ortiz • Edited

Ah, my apologies. I was not clear enough with my phrasing. What I meant is that the async IIFE you're proposing is semantically equivalent to the chained promises I presented in the article—just with some nice syntax. If we were to transform the async IIFE into their chained equivalent, then the points I made in Workaround #1 are still applicable—again, just with nicer syntax.

My main issue lies in the atypical semantics of returning Promise<this> (assuming such syntax exists) from the constructor. Normally, I expect that a class constructor returns an instance of that class. However, using async IIFEs (or chained promises otherwise) goes against this expectation since it does not return this but Promise<this> instead.

The deal breaker, then, is the fact that although this technique is allowed and correct in JavaScript, it is difficult to deny that it is unexpected unless documented. In my opinion, it is best to explicitly tell the user (through the type signature) that the function returns Promise<this> rather than relying on external documentation to know that the constructor produces Promise<this> rather than this.

This is exactly why I recommended static async functions: their type signature does not hide the fact that it returns Promise<this>. This is not the case for the constructor, which TypeScript assumes to (implicitly) return this, not Promise<this>.

Thread Thread
 
fractal profile image
Fractal

Even if it's typed as Awaited<ClassName>?

Thread Thread
 
somedood profile image
Basti Ortiz

As far as I know, TypeScript does not allow the constructor to be directly type-annotated. As of writing, the compiler produces the following error:

Type annotation cannot appear on a constructor declaration.