Does an async function get automatically awaited if called in a constructor?

I am currently working on updating some code due to a library upgrade that requires it to be made async. The code in question has a base class that is inherited by other classes, and I need to call some functions in the constructor that are now asynchronous. I am aware that it is not possible to await the constructor itself, but I have discovered that an async function used inside the constructor is awaited. This seems counterintuitive, but it actually works. Can anyone confirm or explain why this works?

For example:

abstract class Base {
    constructor() {
        // perform 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();

Answer №1

Indeed, it is not possible for constructors to be asynchronous.

An illustration demonstrating the lack of constructor waiting

I have created a simplified version of the original code to clearly display that the constructor does not wait for an asynchronous method. Running this code will result in 'Constructor done' being printed before 'All done', which is outputted by 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();

An alternative approach for achieving a similar behavior to an async constructor

To work around the limitation of constructors being async, one could make the constructor non-invokable and instead create a static method that acts as a factory for generating new instances of the class.

class Dog {
  // prevent 'new Dog()' from being called and throw an error
  constructor() {
    throw new Error('Please call await Dog.new() to create a dog.');
  }
  // a factory method to instantiate a new dog
  static async new(...args) {
    let instance = Object.create(Dog.prototype);
    instance._constructor(...args);
    return instance;
  }
  // the actual constructor logic
  async _constructor(name) {
    this.name = name;
    /* perform asynchronous tasks */
  }
}

// calling new Dog() would result in an error being thrown
async function test() {
  // create instances of dogs using the factory method
  let lassie = await Dog.new('Lassie');
  console.log(lassie.constructor.name);
  console.log(lassie);
}
test();

Answer №2

If you coded the async method test differently, it wouldn't have worked - (setTimeout does not produce a promise).

One approach is to create a private property that fetches the promise within the constructor. Then, mark any method dependent on that state as async. This ensures that the caller does not have to await during instance construction, but only when calling a related method.

Here is a demonstration:

class Test {
    #promise
    
    constructor() {
        this.#promise = this.#asyncMethod();
    }

    async #asyncMethod() {
        const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
        return response.json();
    }
    
    async title() {
        const obj = await this.#promise;
        return obj.title;
    }

    async userId() {
        const obj = await this.#promise;
        return obj.userId;
    }
    
}

// Demonstration
(async () => {
    const instance = new Test();
    const title = await instance.title();
    console.log(title);
    const userId = await instance.userId();
    console.log(userId);
})();

Answer №3

Indeed, it is possible to make constructors asynchronous by simply returning a promise:

#1 — Using the async Keyword

  • Pro: Returns a Promise
  • Pro: Easy to implement
  • Con: Unable to use await
class Base {
    // This constructor is technically asynchronous as it returns a Promise
    constructor() {
        // However, you cannot use `await` within the constructor
        return this.asyncMethod();
    }

    async asyncMethod() {
        // You can use `await` in this method
        return fetch('#');
    }
}

#2 — Truly Asynchronous with async

  • Pro: Returns a Promise
  • Pro: Allows the use of await
  • Con: Requires a more complex implementation
function Base(...args) {
    // This implementation truly makes the function asynchronous and returns a Promise
    async function Base(...args) {
        // You can use `await` within this function
        return await this.asyncMethod();
    }

    Base.prototype.asyncMethod = function asyncMethod() {
        // This method can be made asynchronous by using the `async` keyword
        return fetch('#');
    };

    return Base.call(null, args);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Passport JS allows you to create a secure login form that redirects users to different URIs based on their role

Currently, I am utilizing Passport JS for authentication management and Express JS to handle routing within my application. At the moment, I have a login route that directs to the /teacher URI upon successful authentication (as depicted below). app.post( ...

Error Encountered during Deployment of Custom React App on Heroku due to Fetch API Issue

After developing a small app using React without CRA, I successfully deployed it to Heroku. However, I encountered an issue where a static JSON file that I created isn't fetching properly (it works fine on my local machine). Upon encountering this pr ...

Using Angular to display exclusively the selected items from a list of checkboxes

Is there a way to display only the checked items in a checkbox list? I am looking for a functionality where I can select multiple items from a checkbox list and then have an option to show only the selected items when needed, toggling between showing just ...

Creating a specific quantity of divs with JavaScript

For the past few hours, I've been working hard to solve this problem that has really left me stumped. As far as I can tell, everything is correct. The create function is supposed to generate a certain number of divs specified by the user in an input b ...

Improving the efficiency of AES decryption in Node.js

I had the idea to build a webpage that would display decrypted data fetched from the server. The app.js file on the server reads and decrypts all the data from a specific folder. var http = require('http'); var path = require('path'); ...

nextAuth.js is failing to access the accessToken, returning only the values {iat, exp, jti} instead

Here is the code I am working with: import NextAuth from "next-auth" import CredentialsProvider from "next-auth/providers/credentials" export default NextAuth({ sectret:process.env.NEXTAUTH_SECRET, session: { strategy: "jw ...

Tips on utilizing boolean assignment in a ternary operator with an optional property that is an array in TypeScript

I'm trying to determine the value of an object property based on whether an optional prop is an array. Here's the scenario: const requestingMultipleDevices = Array.isArray(deviceIds); Then I have this object structure: { data: requestingM ...

Create custom AngularJS directives for validation and store them in a variable

Through the use of AngularJS, I've developed a directive called "integer" that invalidates a form if anything other than integers are entered. Because I'm generating the page dynamically by fetching data from the database, it would be helpful to ...

Retrieve the location of the selected element

We are faced with the challenge of determining the position of the button clicked in Angular. Do you think this is achievable? Our current setup involves an ng-grid, where each row contains a button in column 1. When the button is clicked, we aim to displ ...

React Material-ui Dropdown Component

Once I completed my application, I decided to enhance its appearance using Material UI. During the transition from HTML to Material UI, a warning started appearing when selecting an item: index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDO ...

Create dynamic elements within bootstrap-vue using data from a JSON file

Currently, I am utilizing BootstrapVue (Bootstrap 4.6 and VueJS 2)! My objective is to dynamically generate elements such as input fields, dropdowns, and checkboxes based on a JSON file structured like so: [ { "unique_number": "1111", "key": ...

The firebase-generated observable is causing the notorious differ error as it is not iterable

Hey there, I'm encountering an issue that's preventing the route from rendering correctly. I initially thought that unwrapping an observable into an iterable using the async pipe would solve it, but this error indicates otherwise. Sometimes obser ...

Trouble with rendering inline images from markdown files in GatsbyJS

I've been trying to include inline images in my markdown file with the gatsby-remark-images plugin. However, I'm facing an issue where the image is not loading on my local host. I'm not sure if it's a syntax error or if I'm missing ...

Tips for maintaining the state in a React class component for the UI while navigating or refreshing the page

Is there a way to persist the selection stored in state even after page navigation? I have heard that using local storage is a possible solution, which is my preferred method. However, I have only found resources for implementing this in functional compone ...

Ways to stop Bootstrap collapse from displaying depending on a certain condition in bs5 framework

I've been struggling to figure out how to prevent a collapsible panel from opening or showing if a specific value is null. Despite multiple attempts, I haven't had any success. HTML <a href="#" data-bs-toggle="collapse" da ...

Buttons within the Bootstrap carousel caption cannot be clicked

I am currently designing a webpage with a Bootstrap carousel that includes a paragraph caption and two buttons. However, the buttons are not functioning as clickable links - when clicked, nothing happens. {% block body %} <div id ="car-container"> ...

Parcel React component library throws an error stating that "require is not defined

Error message: Uncaught ReferenceError: require is not defined at index.js?5WdmUIncGTkIrWhONvlEDQ:1:1 (anonymous) @ index.js?5WdmUIncGTkIrWhONvlEDQ:1 First lines of index.js: require("./index.css"); var $cI6W1$lodash = require("lodash&q ...

What is the best way to compare dates in order to obtain the necessary results?

Question : Filter the JSON array to retrieve specific entries 1: All entries with name "Sam". 2: All entries with date "Dec 2019". // JSON Data provided below. var data = [{ "id":"27", "0":{ "name":"Sam", "date":"2021-02-28" ...

Ways to stop users from inputting incorrect characters into an input field

I need help with restricting user input in a text field to only allow letters, numbers, and dashes (-). For alphanumerics only - "The alphanumeric character set includes numbers from 0 to 9 and letters from A to Z. In the Perl programming language, the un ...

Is it possible to implement PortalVue in multiple Vue.js single file components?

While working on Vue.js (single file components), I have discovered three methods of passing data around: local state/props, store, and utilizing PortalVue. Through my experiments with PortalVue, I successfully implemented both portal and portal-target wit ...