3
\$\begingroup\$

I have some situations where I run async code inside class constructor:

1. Run async code that returns a response

I execute some async code that returns a response, then I apply then and catch inside class constructor:

class MyClass {

  constructor(
    someParameter,
    anotherParameter,
  ) {
    // Run async code that return a response
    someAsyncCode()
      .then( // do something with result of someAsyncCode )
      .catch( // do something if someAsyncCode goes wrong )  
  }
}

2. Run async code that doesn't return a response

In some situations, the async code doesn't return anything, but I need to run it.

class MyClass {

  constructor(
    someParameter,
    anotherParameter,
  ) {
    // Run async code that doesn't return anything
    someAsyncCode()
  }

  async someAsyncCode() {
    const someAsyncResult = await something();
    // execute some other logic but not return anything
  }
}

Questions

  1. Its a good or bad practice, execute some async code inside class constructor?
  2. in the situation where you need to execute async code that does not return anything, it is necessary to execute then and catch?
  3. what are the implications of executing asyn code without then and catch?
\$\endgroup\$
1
  • \$\begingroup\$ The mistake I usually see with using an async call in a constructor is that the call fetches data... every time an object is created. There's no opportunity for caching, lots of extra HTTP calls, poor UX. Also think about the future: if you have an initializer method that a caller has to call then you decide later that you can do it in the constructor you can make the initializer method a no-op without breaking existing callers. The same is not true if you do work in the constructor and later decide it needs to be an initializer method. \$\endgroup\$ Mar 18 '21 at 2:00
5
\$\begingroup\$

Its a good or bad practice, execute some async code inside class constructor?

Constructors are always synchronous and should succeed to create an object immediately. If your async call doesn't block that and you don't care about the success of your async call, then it is not a bad practice to call the function without awaiting it, and immediately .catch and handle any possible rejections.

In general, if you don't need the return value of an async call and you don't want to wait for it to complete, you should not await it. But, you should still handle rejections with a .catch; even if you do nothing: something().catch(() => {})

in the situation where you need to execute async code that does not return anything, it is necessary to execute then and catch? what are the implications of executing async code without then and catch?

Having a then is unnecessary, and would only be useful if you wanted to do something when the Promise has resolved, but if the Promise can be rejected you must have a catch.

If you do not catch a rejected promise you will have an unhandledPromiseRejection which, if left unhandled, will crash your process in a future version of Node.js.

If you did care about the return value, or you want the caller to be able to handle rejections, you can make the constructor private and then add a static async factory function that uses the private constructor:

class MyClass {

  private constructor(
    someParameter,
    anotherParameter,
  ) {
    // No async calls here
  }

  static async create (
    someParameter,
    anotherParameter,
  ) {
    await something();
    return new MyClass(someParameter, anotherParameter);
  }

}

Then you would use MyClass.create(x,y) to instantiate it, instead of new MyClass(x,y).

\$\endgroup\$
3
\$\begingroup\$

The job of a constructor is to initialize the instance, i.e. put it in a defined state. Using (encapsulated) async code there might result in an instance that is only partially initialized. This would result in the instance being in an undefined state and thusly in code using it, that relies on the instance being fully initialized to fail unpredictably.

On the other hand, if all external interfaces, i.e. methods, properties etc. that the class exhibits defer to any possibly yet unresolved Promises of the async code within the constructor, this effect can be mitigated and might be applied in very special use cases:

class Foo {
    constructor () {
        this.promise = asyncStuff();
    }

    get spamm () {
        return await this.promise;
    }
}

This however will most likely result in very complex code that is hard to maintain.

For the reasons above, I would avoid using async code in constructors if possible.

\$\endgroup\$
3
\$\begingroup\$

Questions.

"Its a good or bad practice, execute some async code inside class constructor?"

Class constructor functions can not be async functions. Thus a object created using the constructor (class syntax) will always immediately return the object.

It then depends on what the code does you are waiting on. If that code is essential for the correct functioning of the object created then you must ensure that it behavior is predicated by the state of the promise/s (data or errors) it is waiting for.

"in the situation where you need to execute async code that does not return anything, it is necessary to execute then and catch?"

If the promise returns no result, does not throw errors, or the completion of the task does not adversely effect the state then there is no need to listen to its then and catch events.

"what are the implications of executing asyn code without then and catch?"

There are no implications assuming there will be no errors to deal with nor data to await.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.