Asynchronous Operations in Constructor

Call asynchronous method in constructor?

The best solution is to acknowledge the asynchronous nature of the download and design for it.

In other words, decide what your application should look like while the data is downloading. Have the page constructor set up that view, and start the download. When the download completes update the page to display the data.

I have a blog post on asynchronous constructors that you may find useful. Also, some MSDN articles; one on asynchronous data-binding (if you're using MVVM) and another on asynchronous best practices (i.e., you should avoid async void).

Asynchronous operations in constructor

It is particularly difficult to put asynchronous operations in a constructor. This is for several reasons:

  1. The constructor needs to return the newly created object so it can't return a promise that would tell you when the async operation is done.
  2. If you do an asynchronous operation inside the constructor that sets some instance data and the constructor returns the object, then you have no way for the calling code to know when the async operation is actually done.

For these reasons, you usually don't want to do an async operation inside a constructor. IMO, the cleanest architecture below is the factory function that returns a promise that resolves to your finished object. You can do as much asynchronous stuff as you want in the factory function (call any methods on the object) and you don't expose the object to the caller until it is fully formed.

These are some of the various options for dealing with that issue:

Use Factory Function that Returns a Promise

This uses a factory function that does some of the more common work for you. It also doesn't reveal the new object until its fully initialized which is a good programming practice as the caller can't accidentally try to use a partially formed object in which the asynchronous stuff hasn't finished yet. The factory function option also cleanly propagates errors (either synchronous or asynchronous) by rejecting the returned promise:

// don't make this class definition public so the constructor is not public
class MyObj() {
constructor(someValue) {
this.someProp = someValue;
}
init() {
return Service.getService().then(val => {
this.asyncProp = val;
return this;
});
}
}

function createMyObj(someValue) {
let x = new MyObj(someVal);
return x.init();
}

createMyObj(someVal).then(obj => {
// obj ready to use and fully initialized here
}).catch(err => {
// handle error here
});

If you're using modules, you can export only the factory function (no need to export the class itself) and thus enforce that the object is initialized properly and not used until that initialization is done.

Break async object initialization into a separate method that can return a promise

class MyObj() {
constructor(someValue) {
this.someProp = someValue;
}
init() {
return Service.getService().then(val => {
this.asyncProp = val;
});
}
}

let x = new MyObj(someVal);
x.init().then(() => {
// ready to use x here
}).catch(err => {
// handle error
});

Use Events to Signal Completion

This scheme is used in a lot of I/O related APIs. The general idea is that you return an object from the constructor, but the caller knows that object hasn't really completed its initialization until a particular event occurs.

// object inherits from EventEmitter
class MyObj extends EventEmitter () {
constructor(someValue) {
this.someProp = someValue;

Service.getService().then(val => {
this.asyncProp = val;
// signal to caller that object has finished initializing
this.emit('init', val);
});
}
}

let x = new MyObj(someVal);
x.on('init', () => {
// object is fully initialized now
}).on('error', () => {
// some error occurred
});

Hackish way to put the Async Operation in the Constructor

Though I wouldn't recommend using this technique, this is what it would take to put the async operation in the actual constructor itself:

class MyObj() {
constructor(someValue) {
this.someProp = someValue;
this.initPromise = Service.getService().then(val => {
this.asyncProp = val;
});
}
}

let x = new MyObj(someVal);
x.initPromise.then(() => {
// object ready to use now
}).catch(err => {
// error here
});

Note, you see the first design pattern in many places in various APIs. For example, for a socket connection in node.js, you would see this:

let socket = new net.Socket(...);
socket.connect(port, host, listenerCallback);

The socket is created in the first step, but then connected to something in the second step. And, then the same library has a factory function net.createConnection() which combines those two steps into one function (an illustration of the second design pattern above). The net module examples don't happen to use promises (very few nodejs original apis do), but they accomplish the same logic using callbacks and events.


Other note on your code

You likely also have an issue with the value of this in your code. A .then() handler does not naturally preserve the value of this from the surrounding environment if you pass it a regular function() {} reference. So, in this:

function Constructor(){
Service.getService().then(function(data){
this.arr = data.data.array;
return this.arr
})
}

The value of this when you try to do this.arr = data.data.array; is not going to be correct. The simplest way to fix that issue in ES6 is to use a fat arrow function instead:

function Constructor(){
Service.getService().then(data => {
this.arr = data.data.array;
return this.arr
});
}

Async/Await Class Constructor

This can never work.

The async keyword allows await to be used in a function marked as async but it also converts that function into a promise generator. So a function marked with async will return a promise. A constructor on the other hand returns the object it is constructing. Thus we have a situation where you want to both return an object and a promise: an impossible situation.

You can only use async/await where you can use promises because they are essentially syntax sugar for promises. You can't use promises in a constructor because a constructor must return the object to be constructed, not a promise.

There are two design patterns to overcome this, both invented before promises were around.

  1. Use of an init() function. This works a bit like jQuery's .ready(). The object you create can only be used inside its own init or ready function:

Usage:

    var myObj = new myClass();
myObj.init(function() {
// inside here you can use myObj
});

Implementation:

    class myClass {
constructor () {

}

init (callback) {
// do something async and call the callback:
callback.bind(this)();
}
}

  1. Use a builder. I've not seen this used much in javascript but this is one of the more common work-arounds in Java when an object needs to be constructed asynchronously. Of course, the builder pattern is used when constructing an object that requires a lot of complicated parameters. Which is exactly the use-case for asynchronous builders. The difference is that an async builder does not return an object but a promise of that object:

Usage:

    myClass.build().then(function(myObj) {
// myObj is returned by the promise,
// not by the constructor
// or builder
});

// with async/await:

async function foo () {
var myObj = await myClass.build();
}

Implementation:

    class myClass {
constructor (async_param) {
if (typeof async_param === 'undefined') {
throw new Error('Cannot be called directly');
}
}

static build () {
return doSomeAsyncStuff()
.then(function(async_result){
return new myClass(async_result);
});
}
}

Implementation with async/await:

    class myClass {
constructor (async_param) {
if (typeof async_param === 'undefined') {
throw new Error('Cannot be called directly');
}
}

static async build () {
var async_result = await doSomeAsyncStuff();
return new myClass(async_result);
}
}

Note: although in the examples above we use promises for the async builder they are not strictly speaking necessary. You can just as easily write a builder that accept a callback.



Note on calling functions inside static functions.

This has nothing whatsoever to do with async constructors but with what the keyword this actually mean (which may be a bit surprising to people coming from languages that do auto-resolution of method names, that is, languages that don't need the this keyword).

The this keyword refers to the instantiated object. Not the class. Therefore you cannot normally use this inside static functions since the static function is not bound to any object but is bound directly to the class.

That is to say, in the following code:

class A {
static foo () {}
}

You cannot do:

var a = new A();
a.foo() // NOPE!!

instead you need to call it as:

A.foo();

Therefore, the following code would result in an error:

class A {
static foo () {
this.bar(); // you are calling this as static
// so bar is undefinned
}
bar () {}
}

To fix it you can make bar either a regular function or a static method:

function bar1 () {}

class A {
static foo () {
bar1(); // this is OK
A.bar2(); // this is OK
}

static bar2 () {}
}

Optimising an asynchronous call in a constructor using JoinableTaskFactory.Run

Below is the best set of conclusions that I could derive from all the comments on the question.

Point #1: Injection Constructors should be simple

Injection constructors should be simple, and in the context of Fluent Validation, the MustAsync method should help to simplify asynchronous validation.

Point #2: Warning not applicable to .NET Core

While I can use JoinableTaskFactory, I should not be getting this warning, because the problem it is trying to solve is not applicable to .NET Core.

Below is the description from the documentation for JoinableTaskFactory. It applies to situations where joining threads to the main thread could cause deadlocks.

A factory for starting asynchronous tasks that can mitigate deadlocks when the tasks require the Main thread of an application and the Main thread may itself be blocking on the completion of a task.

Thus, one can safely ignore the warning in the cases where it is simply not possible to rewrite the code. (Note the use of Task.Run(), which is the recommended way to launch a compute-bound task as of .NET Framework 4.5.)

#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
Task.Run(() => MethodAsync()).Wait();
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits

Point #3: How to implement JoinableTaskFactory

If you want to use JoinableTaskFactory anyway, this is taken from comments on another related answer.

For .NET Framework projects, use ThreadHelper.JoinableTaskFactory.

For .NET Core projects, if you want to use JoinableTaskFactory, do not create a new context every time you use it. You should create one shared JoinableTaskContext object and use that one object throughout the application (per request or per process). This is the case when SynchronizationContext.Current == null.

Point #4: Visual Studio Warnings

The warning comes from the NuGet package Microsoft.VisualStudio.Threading.Analyzers. It is useful to keep it installed, because it will also warn you when you use code like MethodAsync().Result or MethodAsync().Wait() when you should instead be using await MethodAsync().

Can we call an async method from a constructor?

You cannot have async constructors.

One alternative is to have async factory methods:

public class MyModel
{
public SFOpportunity Opportunity { get; set; }
private MyModel() { }

public static async Task<MyModel> CreateAsync(string id)
{
var result = new MyModel();
await result.setOpportunityAsync(id);
return result;
}

private async Task setOpportunityAsync(string id)
{
...
}
}

Can constructors be async?

Constructor acts very similarly to a method returning the constructed type. And async method can't return just any type, it has to be either “fire and forget” void, or Task.

If the constructor of type T actually returned Task<T>, that would be very confusing, I think.

If the async constructor behaved the same way as an async void method, that kind of breaks what constructor is meant to be. After constructor returns, you should get a fully initialized object. Not an object that will be actually properly initialized at some undefined point in the future. That is, if you're lucky and the async initialization doesn't fail.

All this is just a guess. But it seems to me that having the possibility of an async constructor brings more trouble than it's worth.

If you actually want the “fire and forget” semantics of async void methods (which should be avoided, if possible), you can easily encapsulate all the code in an async void method and call that from your constructor, as you mentioned in the question.

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


Related Topics



Leave a reply



Submit