async constructor functions in TypeScript? async constructor functions in TypeScript? typescript typescript

async constructor functions in TypeScript?


A constructor must return an instance of the class it 'constructs'. Therefore, it's not possible to return Promise<...> and await for it.

You can:

  1. Make your public setup async.

  2. Do not call it from the constructor.

  3. Call it whenever you want to 'finalize' object construction.

    async function run() {    let topic;    debug("new TopicsModel");    try     {        topic = new TopicsModel();        await topic.setup();    }     catch (err)     {        debug("err", err);    }}


Readiness design pattern

Don't put the object in a promise, put a promise in the object.

Readiness is a property of the object. So make it a property of the object.

The awaitable initialise method described in the accepted answer has a serious limitation. Using await means only one block of code can wait on the action. This is fine for code with guaranteed linear execution but in multi-threaded or event driven code it's untenable.

The problem is more tractable when correctly framed. The objective is not to wait on construction but to wait on readiness of the constructed object. These are two completely different things. It is even possible for something like a database connection object to be in a ready state, go back to a non-ready state, then become ready again.

How can we determine readiness if it depends on activities that may not be complete when the constructor returns? Quite obviously readiness is a property of the object. Many frameworks directly express the notion of readiness. In JavaScript we have the Promise, and in C# we have the Task. Both have direct language support for object properties.

Expose the construction completion promise as a property of the constructed object. When the asynchronous part of your construction finishes it should resolve the promise.

It doesn't matter whether .then(...) executes before or after the promise resolves. The promise specification states that invoking then on an already resolved promised simply executes the handler immediately.

class Foo {  public Ready: Promise.IThenable<any>;  constructor() {    ...    this.Ready = new Promise((resolve, reject) => {      $.ajax(...).then(result => {        // use result        resolve(undefined);      }).fail(reject);    });  }}var foo = new Foo();foo.Ready.then(() => {  //do stuff that needs foo to be ready, eg apply bindings});

Why resolve(undefined); instead of resolve();? Because ES6. Adjust as required to suit your target.

From the peanut gallery

Using await

In a comment it has been suggested that I should have framed this solution with await to more directly address the question as asked.

This is a poor solution because it permits only the code in the scope immediately following the await statement to wait on completion. Exposing a promise object as a property of an asynchronously initialised object means any code anywhere can guarantee that initialisation is complete because the promise is in scope everywhere the object is in scope, so it is guaranteed available everywhere that the risk exists.

Besides, it is unlikely that using the await keyword is a deliverable for any project that isn't a university assignment demonstrating use of the await keyword.

Lack of enforcement as compared to factory pattern

One punter thinks this pattern "is a bad idea because without a factory function, there's nothing to enforce the invariant of checking the readiness. It's left to the clients, which you can practically guarantee will mess up from time to time."

How will he stop people from building factory methods that don't enforce the check? Where do you draw the line? The answer is you learn the difference between domain specific code and framework code and apply different standards, seasoned with some common sense: would you forbid the division operator because there's nothing stopping people from passing a zero divisor?


This is original work by me. I devised this design pattern because I was unsatisfied with external factories and other such workarounds. Despite searching for some time, I found no prior art for my solution, so I'm claiming credit as the originator of this pattern until disputed.

In 2020 I discovered that in 2013 Stephen Cleary posted a very similar solution to the problem. Looking back through my own work the first vestiges of this approach appear in code I worked on around the same time. I suspect Cleary put it all together first but he didn't formalise it as a design pattern or publish it where it would be easily found by others with the problem. Moreover, Cleary deals only with construction which is only one application of the Readiness pattern (see below).

In a comment, @suhas suggests the use of await rather than .then and this would work but it's less broadly compatible. On the matter of compatibility, Typescript has changed since I wrote this, and now you would have to declare public Ready: Promise<any>

Awaitable initialise method versus promise

Using await means only one block of code can have a readiness guarantee.

How can you fix this? Instead of using await, capture the returned Task and use continuations everywhere. If you do that, you then have the problem of associating the task with the object for which it represents readiness. The solution for this is to have the constructor call the initialiser and put the task into a property of the object. At this point you have invented the Readiness pattern.

There's one other variation. Instead of launching the initialiser from the constructor you could do it from the property getter (lazy load). It's still the Readiness pattern.

Summary

The pattern is

  • put a promise in the object it describes
  • expose it as a property named Ready
  • always reference the promise via the Ready property (don't capture it in a client code variable)

This establishes clear simple semantics and guarantees that

  • the promise will be created and managed
  • the promise has identical scope to the object it describes
  • the semantics of readiness dependence are conspicuous and clear in client code
  • if the promise is replaced (eg a connection goes unready then ready again due to network conditions) client code referring to it via thing.Ready will always use the current promise

This last one is a nightmare until you use the pattern and let the object manage its own promise. It's also a very good reason to refrain from capturing the promise into a variable.

Some objects have methods that temporarily put them in an invalid condition, and the pattern can serve in that scenario without modification. Code of the form obj.Ready.then(...) will always use whatever promise property is returned by the Ready property, so whenever some action is about to invalidate object state, a fresh promise can be created.

Closing notes

The Readiness pattern isn't specific to construction. It is easily applied to construction but it's really about ensuring that state dependencies are met. In these days of asynchronous code you need a system, and the simple declarative semantics of a promise make it straightforward to express the idea that an action should be taken ASAP, with emphasis on possible. Once you start framing things in these terms, arguments about long running methods or constructors become moot.

Deferred initialisation still has its place; as I mentioned you can combine Readiness with lazy load. But if chances are that you won't use the object, then why create it early? It might be better to create on demand.

There are other solutions. When I write embedded software I create everything up front including resource pools. This makes leaks impossible and memory demands are known at compile time. But that's only a solution for a small closed problem space.


Use an asynchronous factory method instead.

class MyClass {   private mMember: Something;   constructor() {      this.mMember = await SomeFunctionAsync(); // error   }}

Becomes:

class MyClass {   private mMember: Something;   // make private if possible; I can't in TS 1.8   constructor() {   }   public static CreateAsync = async () => {      const me = new MyClass();            me.mMember = await SomeFunctionAsync();      return me;   };}

This will mean that you will have to await the construction of these kinds of objects, but that should already be implied by the fact that you are in the situation where you have to await something to construct them anyway.

There's another thing you can do but I suspect it's not a good idea:

// probably BADclass MyClass {   private mMember: Something;   constructor() {      this.LoadAsync();   }   private LoadAsync = async () => {      this.mMember = await SomeFunctionAsync();   };}

This can work and I've never had an actual problem from it before, but it seems to be dangerous to me, since your object will not actually be fully initialized when you start using it.

Another way to do it, which might be better than the first option in some ways, is to await the parts, and then construct your object after:

export class MyClass {   private constructor(      private readonly mSomething: Something,      private readonly mSomethingElse: SomethingElse   ) {   }   public static CreateAsync = async () => {      const something = await SomeFunctionAsync();      const somethingElse = await SomeOtherFunctionAsync();      return new MyClass(something, somethingElse);   };}