异步函数在构造函数中调用时会自动等待吗?

huangapple go评论137阅读模式
英文:

Is async function awaited automatically if call in constructor?

问题

以下是您要翻译的内容:

"我正在处理一些需要更改为异步的代码(因为库升级的原因)。我的代码具有相同的基类,其他类继承自该基类,我想在构造函数中调用一些现在是异步的函数。我知道无法等待构造函数,但我发现在构造函数中使用的异步函数会被等待。从逻辑上讲,它不应该起作用,但它确实起作用。有人可以确认并解释为什么它起作用吗?

示例:

abstract class Base {
    constructor() {
        // 进行一些设置
        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();

"

英文:

I'm working on some code which needs to be changed to be async (because of library upgrade). My code has same base class which is inherited by other classes and I want to call some functions in constructor which are async now. As I know it's impossible to await constructor but what I found async function used inside constructor is awaited. From logical point it shouldn't work but it works. Can someone confirm explain why it works?

Example

abstract class Base {
    constructor() {
        // do some setup
        this.asyncMethod();
    }

    abstract asyncMethod(): Promise&lt;void&gt;;
}

class Test extends Base {
    constructor() {
        super();
    }

    async asyncMethod(): Promise&lt;void&gt; {
        await setTimeout(() =&gt; console.log(&#39;Tadaaaa&#39;));
    }
}

const instance = new Test();

答案1

得分: 1

No it is not. And constructors can't be async.

A test that shows the constructor isn't waiting

我简化了问题中的代码,创建了一个示例,清楚地显示构造函数不会等待异步方法。当您运行此代码时,您将在从构造函数获取“Constructor done”之前从异步方法中写入的“All done”。

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

A workaround if you need something similar to an async constructor

如果您需要类似于异步构造函数的东西,可以使用以下方法绕过。创建一个无法调用的构造函数,并创建一个静态方法作为工厂,用于创建类的新实例。

class Dog {
  // make 'new Dog()' throw an error
  constructor() {
    throw new Error('Please call await Dog.new() to create a dog.');
  }
  // a factory method that creates a dog
  static async new(...args) {
    let instance = Object.create(Dog.prototype);
    instance._constructor(...args);
    return instance;
  }
  // our 'real' constructor
  async _constructor(name) {
    this.name = name;
    /* do async stuff */
  }
}

// new Dog(); -> would throw an error
async function test() {
  // instead create dog instances like this
  let lassie = await Dog.new('Lassie');
  console.log(lassie.constructor.name);
  console.log(lassie);
}
test();
英文:

No it is not. And constructors can't be async.

A test that shows the constructor isn't waiting

I simplified the code from the question into an example that clearly shows that the constructor does not wait for an async method. When yo u run this code you will see that you get 'Constructor done' from the constructor before 'All done' that is written from the async method.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class MyClass {
  constructor() {
    this.asyncMethod();
    console.log(&#39;Constructor done&#39;);
  }

  sleep(ms) {
    return new Promise(res =&gt; setTimeout(res, ms));
  }

  async asyncMethod() {
    await this.sleep(500);
    console.log(&#39;All done&#39;);
  }
}

const instance = new MyClass();

<!-- end snippet -->

A workaround if you need something similar to an async constructor

A workaround would be to make the constructor uncallable and make a static method that is a factory that creates new instances of the class.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class Dog {
  // make &#39;new Dog()&#39; throw an error
  constructor() {
    throw new Error(&#39;Please call await Dog.new() to create a dog.&#39;);
  }
  // a factory method that creates a dog
  static async new(...args) {
    let instance = Object.create(Dog.prototype);
    instance._constructor(...args);
    return instance;
  }
  // our &#39;real&#39; constructor
  async _constructor(name) {
    this.name = name;
    /* do async stuff */
  }
}

// new Dog(); -&gt; would throw an error
async function test() {
  // instead create dog instances like this
  let lassie = await Dog.new(&#39;Lassie&#39;);
  console.log(lassie.constructor.name);
  console.log(lassie);
}
test();

<!-- end snippet -->

答案2

得分: 0

如果你正确设置了异步方法测试,它就不会工作(setTimeout 不返回一个 promise)。

作为一种替代方法,你可以在构造函数中设置一个私有属性,该属性获取 promise。然后将依赖于该状态的任何方法设置为 async。这样调用者在实例构建时不会等待,而是在调用依赖方法时等待。

示例:

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

// 示例
(async () => {
    const instance = new Test();
    const title = await instance.title();
    console.log(title);
    const userId = await instance.userId();
    console.log(userId);
})();
英文:

If you would have set up the async method test correctly, it wouldn't have worked (setTimeout doesn't return a promise).

As an alternative you can have a private property that gets the promise in the constructor. Then make any method that depends on that state async. This way the caller will not await during construction of the instance, but when calling a dependent method.

Demo:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

    async #asyncMethod() {
        const response = await fetch(&#39;https://jsonplaceholder.typicode.com/todos/1&#39;);
        return response.json();
    }
    
    async title() {
        // Every method that needs the async state needs to await it
        const obj = await this.#promise;
        return obj.title;
    }

    async userId() {
        // Every method that needs the async state needs to await it
        const obj = await this.#promise;
        return obj.userId;
    }
    
}

// Demo
(async () =&gt; {
    const instance = new Test();
    const title = await instance.title();
    console.log(title);
    const userId = await instance.userId();
    console.log(userId);
})();

<!-- end snippet -->

答案3

得分: -2

实际上... 你可以通过简单地返回承诺来使构造函数异步

### #1 &amp;mdash; 技术上是 `async`

- 优点: 返回一个 `Promise`
- 优点: 容易编写
- 缺点: 无法使用 `await`

```javascript
class Base {
    // 从技术上讲是异步的——因为它返回一个Promise
    // 但是,我假设你的意思是在其中可以使用 `await`——答案是否定的,参见方法 #2
    constructor() {
        // 你不能在这里的构造函数内部使用 `await` :(
        return this.asyncMethod();
    }

    async asyncMethod() {
        // 你可以在这里使用 `await` :)
        return fetch('#');
    }
}

#2 &mdash; 实际上是 async

  • 优点: 返回一个 Promise
  • 优点: 可以使用 await
  • 缺点: 必须设计得过度复杂
function Base(...args) {
    // 这是真正的异步——它是一个Promise
    async function Base(...args) {
        // 你可以在这里使用 `await`! :D
        return await this.asyncMethod();
    }

    Base.prototype.asyncMethod = function asyncMethod() {
        // 你可以将这个改为 `async`
        return fetch('#');
    };

    return Base.call(null, args);
}
英文:

Actually... You can make constructors async by simply returning the promise:

#1 &mdash; Technically async

  • Pro: returns a Promise
  • Pro: Easy to write
  • Con: Cannot use await
class Base {
    // This is technically async--since it returns a Promise
    // But, I&#39;m assuming you meant if you could use `await` within--which is no, see method #2
    constructor() {
        // You can NOT use `await` within the constructor here :(
        return this.asyncMethod();
    }

    async asyncMethod() {
        // You CAN use `await` here :)
        return fetch(&#39;#&#39;);
    }
}

#2 &mdash; Actually async

  • Pro: returns a Promise
  • Pro: can use await
  • Con: Has to be overengineered
function Base(...args) {
    // This is truly async--it is a promise
    async function Base(...args) {
        // You can use `await` here! :D
        return await this.asyncMethod();
    }

    Base.prototype.asyncMethod = function asyncMethod() {
        // You can change this to be `async`
        return fetch(&#39;#&#39;);
    };

    return Base.call(null, args);
}

huangapple
  • 本文由 发表于 2023年5月7日 02:23:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76190466.html