英文:
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<void>;
}
class Test extends Base {
constructor() {
super();
}
async asyncMethod(): Promise<void> {
await setTimeout(() => console.log('Tadaaaa'));
}
}
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('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();
<!-- 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 '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();
<!-- 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('https://jsonplaceholder.typicode.com/todos/1');
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 () => {
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 &mdash; 技术上是 `async`
- 优点: 返回一个 `Promise`
- 优点: 容易编写
- 缺点: 无法使用 `await`
```javascript
class Base {
// 从技术上讲是异步的——因为它返回一个Promise
// 但是,我假设你的意思是在其中可以使用 `await`——答案是否定的,参见方法 #2
constructor() {
// 你不能在这里的构造函数内部使用 `await` :(
return this.asyncMethod();
}
async asyncMethod() {
// 你可以在这里使用 `await` :)
return fetch('#');
}
}
#2 — 实际上是 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 — 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'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('#');
}
}
#2 — 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('#');
};
return Base.call(null, args);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论