如何在 Javascript 异步函数中 return Promise?为什么 Async 方法不包装 returned Promise?
How to return a Promise in a Javascript async function? Why does Async method not wrap the returned Promise?
我有以下代码。我的意图是开始一个任务(在它真正开始之前涉及一些 await
调用)。当它完成 starting 它时,我需要更新 UI 以告诉用户任务已经开始并正在等待结果。结果稍后会出现,所以我想 return 另一个 Promise
这样应用程序知道何时用 returned 结果更新 UI:
async function startSomethingAsync() {
await new Promise(r => globalThis.setTimeout(r, 1000));
console.log("Task started");
const finishedPromise = new Promise<number>(r => {
globalThis.setTimeout(() => r(100), 3000);
});
return finishedPromise;
}
(async() => {
// resultPromise is number instead of Promise<number>
const resultPromise = await startSomethingAsync();
// My intention: update UI after starting the task
console.log("Task started");
// Then when the result is there, do something else
// Currently this is not compiled because resultPromise is not a Promise
resultPromise.then(r => console.log("Result arrived: " + r));
})();
但是上面的代码不起作用。不知何故 resultPromise
是 number
(解析后为 100)而不是 Promise<number>
。 TypeScript 还识别 startSomethingAsync
returning Promise<number>
而不是 Promise<Promise<number>>
.
为什么会这样? async
方法不应该在 returned Promise 之外包装另一个 Promise 吗?我如何实现我想要做的事情,这是一个“好的”模式吗?
我什至试过自己包起来:
async function startSomethingAsync() {
await new Promise(r => globalThis.setTimeout(r, 1000));
console.log("Task started");
const finishedPromise = new Promise<Promise<number>>(r1 => r1(new Promise<number>(r2 => {
globalThis.setTimeout(() => r2(100), 3000);
})));
return finishedPromise;
}
函数仍然是 returns Promise<number>
而不是 Promise<Promise<number>>
.
所有 async
函数都隐含地 return 一个承诺。如果您明确 return 一个承诺,它不会被包装。
如果一个 promise 解决了另一个 promise,它们的嵌套 promise 将被展平 -
(new Promise(r =>
r(new Promise(r2 =>
r2("hello")
))
))
.then(console.log).catch(console.error)
Why is this happening? Shouldn't the async method wrap another Promise outside of the returned Promise?
没有。如果您 return 来自 async
函数的承诺,通过调用 async
函数创建的承诺只是 解析为 该承诺(它等待其他承诺解决并将其实现或拒绝视为自己的承诺;更多关于我博客上的承诺术语 here)。它没有设置要用承诺来实现的承诺。承诺是 never 履行承诺(无论 async
功能)。
How do I achieve what I am trying to do and is it a "good" pattern?
如果您想使用 startSomethingAsync
中的承诺,只需不要 await
它:
(async() => {
const resultPromise = startSomethingAsync();
// ^−−− no `await`
// My intention: update UI after starting the task
console.log("Task started");
// Wait until it's finished
const r = await resultPromise;
// ^−−− *now* we `await` await it
console.log("Result arrived: " + r);
})();
请注意,如果 startSomethingAsync
在开始工作之前有一个异步延迟(这很不寻常),“任务已启动”日志将在该异步延迟发生之前出现。如果你不想要那个,把那个部分从函数中分离出来,这样你就可以在中间记录你的“任务开始”。例如,您可能有一个单独的函数来执行初始位,然后将其中的任何内容传递给 startSomethingAsync
:
(async() => {
const resultPromise = startSomethingAsync(await theStuffBefore());
// ^^^^^
// My intention: update UI after starting the task
console.log("Task started");
// Wait until it's finished
const r = await resultPromise;
// ^−−− *now* we `await` await it
console.log("Result arrived: " + r);
})();
或者,如果您的流程是 multi-step,每个步骤都可以 return 一个函数(或一个带有方法的对象)用于下一步:
(async() => {
const nextStep = await startSomethingAsync();
const resultPromise = nextStep(); // No `await`
// My intention: update UI after starting the task
console.log("Task started");
// Wait until it's finished
const r = await resultPromise;
console.log("Result arrived: " + r);
})();
(事实上,这与使用异步生成器非常相似。)
这些都是同一个主题的变体,但 take-away 传达的信息是,一个承诺永远无法用另一个承诺来实现。您会注意到从 new Promise
获得的函数的标准名称是 resolve
和 reject
,而不是 fulfill
和 reject
。那是因为当你将一个 promise 传递给 resolve
(或 return 一个来自 async
函数的 promise,这实际上是同一件事),它 解析 承诺 到 另一个承诺,而不是用另一个承诺履行承诺。
正如我们在评论中讨论的那样 - 您必须将生成的承诺包装在函数或对象中。
async function startSomethingAsync() {
await new Promise(r => setTimeout(r, 1000));
const finishedPromise = new Promise(r => {
setTimeout(() => r(100), 3000);
});
return () => finishedPromise;
}
(async () => {
const resultPromise = await startSomethingAsync();
resultPromise().then((r) => console.log("Result arrived: " + r));
})();
我有以下代码。我的意图是开始一个任务(在它真正开始之前涉及一些 await
调用)。当它完成 starting 它时,我需要更新 UI 以告诉用户任务已经开始并正在等待结果。结果稍后会出现,所以我想 return 另一个 Promise
这样应用程序知道何时用 returned 结果更新 UI:
async function startSomethingAsync() {
await new Promise(r => globalThis.setTimeout(r, 1000));
console.log("Task started");
const finishedPromise = new Promise<number>(r => {
globalThis.setTimeout(() => r(100), 3000);
});
return finishedPromise;
}
(async() => {
// resultPromise is number instead of Promise<number>
const resultPromise = await startSomethingAsync();
// My intention: update UI after starting the task
console.log("Task started");
// Then when the result is there, do something else
// Currently this is not compiled because resultPromise is not a Promise
resultPromise.then(r => console.log("Result arrived: " + r));
})();
但是上面的代码不起作用。不知何故 resultPromise
是 number
(解析后为 100)而不是 Promise<number>
。 TypeScript 还识别 startSomethingAsync
returning Promise<number>
而不是 Promise<Promise<number>>
.
为什么会这样? async
方法不应该在 returned Promise 之外包装另一个 Promise 吗?我如何实现我想要做的事情,这是一个“好的”模式吗?
我什至试过自己包起来:
async function startSomethingAsync() {
await new Promise(r => globalThis.setTimeout(r, 1000));
console.log("Task started");
const finishedPromise = new Promise<Promise<number>>(r1 => r1(new Promise<number>(r2 => {
globalThis.setTimeout(() => r2(100), 3000);
})));
return finishedPromise;
}
函数仍然是 returns Promise<number>
而不是 Promise<Promise<number>>
.
所有 async
函数都隐含地 return 一个承诺。如果您明确 return 一个承诺,它不会被包装。
如果一个 promise 解决了另一个 promise,它们的嵌套 promise 将被展平 -
(new Promise(r =>
r(new Promise(r2 =>
r2("hello")
))
))
.then(console.log).catch(console.error)
Why is this happening? Shouldn't the async method wrap another Promise outside of the returned Promise?
没有。如果您 return 来自 async
函数的承诺,通过调用 async
函数创建的承诺只是 解析为 该承诺(它等待其他承诺解决并将其实现或拒绝视为自己的承诺;更多关于我博客上的承诺术语 here)。它没有设置要用承诺来实现的承诺。承诺是 never 履行承诺(无论 async
功能)。
How do I achieve what I am trying to do and is it a "good" pattern?
如果您想使用 startSomethingAsync
中的承诺,只需不要 await
它:
(async() => {
const resultPromise = startSomethingAsync();
// ^−−− no `await`
// My intention: update UI after starting the task
console.log("Task started");
// Wait until it's finished
const r = await resultPromise;
// ^−−− *now* we `await` await it
console.log("Result arrived: " + r);
})();
请注意,如果 startSomethingAsync
在开始工作之前有一个异步延迟(这很不寻常),“任务已启动”日志将在该异步延迟发生之前出现。如果你不想要那个,把那个部分从函数中分离出来,这样你就可以在中间记录你的“任务开始”。例如,您可能有一个单独的函数来执行初始位,然后将其中的任何内容传递给 startSomethingAsync
:
(async() => {
const resultPromise = startSomethingAsync(await theStuffBefore());
// ^^^^^
// My intention: update UI after starting the task
console.log("Task started");
// Wait until it's finished
const r = await resultPromise;
// ^−−− *now* we `await` await it
console.log("Result arrived: " + r);
})();
或者,如果您的流程是 multi-step,每个步骤都可以 return 一个函数(或一个带有方法的对象)用于下一步:
(async() => {
const nextStep = await startSomethingAsync();
const resultPromise = nextStep(); // No `await`
// My intention: update UI after starting the task
console.log("Task started");
// Wait until it's finished
const r = await resultPromise;
console.log("Result arrived: " + r);
})();
(事实上,这与使用异步生成器非常相似。)
这些都是同一个主题的变体,但 take-away 传达的信息是,一个承诺永远无法用另一个承诺来实现。您会注意到从 new Promise
获得的函数的标准名称是 resolve
和 reject
,而不是 fulfill
和 reject
。那是因为当你将一个 promise 传递给 resolve
(或 return 一个来自 async
函数的 promise,这实际上是同一件事),它 解析 承诺 到 另一个承诺,而不是用另一个承诺履行承诺。
正如我们在评论中讨论的那样 - 您必须将生成的承诺包装在函数或对象中。
async function startSomethingAsync() {
await new Promise(r => setTimeout(r, 1000));
const finishedPromise = new Promise(r => {
setTimeout(() => r(100), 3000);
});
return () => finishedPromise;
}
(async () => {
const resultPromise = await startSomethingAsync();
resultPromise().then((r) => console.log("Result arrived: " + r));
})();