自 ES2015 引入 Promise 以来,JavaScript 仅支持两种 Promise 组合器:静态方法 Promise.all
和 Promise.race
。
目前有两个新的提案正在标准化过程中:Promise.allSettled
和 Promise.any
。随着这些添加,JavaScript 中将总共有四种 Promise 组合器,每种都支持不同的用例。
以下是四种组合器的概述
名称 | 描述 | 状态 |
---|---|---|
Promise.allSettled | 不短路 | 已添加到 ES2020 ✅ |
Promise.all | 当输入值被拒绝时短路 | 已添加到 ES2015 ✅ |
Promise.race | 当输入值被确定时短路 | 已添加到 ES2015 ✅ |
Promise.any | 当输入值被完成时短路 | 已添加到 ES2021 ✅ |
让我们看看每个组合器的一个示例用例。
Promise.all
#
- Chrome: 自版本 32 起支持
- Firefox: 自版本 29 起支持
- Safari: 自版本 8 起支持
- Node.js: 自版本 0.12 起支持
- Babel: 支持
Promise.all
让你知道所有输入 Promise 都已完成,或者其中一个被拒绝。
想象一下,用户点击一个按钮,你想加载一些样式表以便渲染一个全新的 UI。这个程序并行地为每个样式表启动一个 HTTP 请求。
const promises = [
fetch('/component-a.css'),
fetch('/component-b.css'),
fetch('/component-c.css'),
];
try {
const styleResponses = await Promise.all(promises);
enableStyles(styleResponses);
renderNewUi();
} catch (reason) {
displayError(reason);
}
你只想在所有请求成功后才开始渲染新的 UI。如果出现错误,你希望尽快显示错误消息,而无需等待其他任何工作完成。
在这种情况下,你可以使用 Promise.all
:你希望知道所有 Promise 都已完成,或者只要其中一个被拒绝。
Promise.race
#
- Chrome: 自版本 32 起支持
- Firefox: 自版本 29 起支持
- Safari: 自版本 8 起支持
- Node.js: 自版本 0.12 起支持
- Babel: 支持
Promise.race
在你想要运行多个 Promise 时很有用,并且要么…
- 对第一个成功的结果做一些操作(如果其中一个 Promise 完成),或者
- 只要其中一个 Promise 被拒绝就做一些操作。
也就是说,如果其中一个 Promise 被拒绝,你希望保留该拒绝以单独处理错误情况。以下示例正是这样做的
try {
const result = await Promise.race([
performHeavyComputation(),
rejectAfterTimeout(2000),
]);
renderResult(result);
} catch (error) {
renderError(error);
}
我们启动一个计算量大的任务,它可能需要很长时间,但我们将其与一个在 2 秒后被拒绝的 Promise 进行竞赛。根据第一个完成或被拒绝的 Promise,我们要么渲染计算结果,要么渲染错误消息,在两个单独的代码路径中。
Promise.allSettled
#
- Chrome: 自版本 76 起支持
- Firefox: 自版本 71 起支持
- Safari: 自版本 13 起支持
- Node.js: 自版本 12.9.0 起支持
- Babel: 支持
Promise.allSettled
在所有输入 Promise 都确定时给你一个信号,这意味着它们要么完成,要么被拒绝。这在你不关心 Promise 状态的情况下很有用,你只想在工作完成时知道,无论它是否成功。
例如,你可以启动一系列独立的 API 调用,并使用 Promise.allSettled
确保它们都完成,然后再执行其他操作,例如删除加载动画。
const promises = [
fetch('/api-call-1'),
fetch('/api-call-2'),
fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.
await Promise.allSettled(promises);
// All API calls have finished (either failed or succeeded).
removeLoadingIndicator();
Promise.any
#
- Chrome: 自版本 85 起支持
- Firefox: 自版本 79 起支持
- Safari: 自版本 14 起支持
- Node.js: 自版本 16 起支持
- Babel: 支持
Promise.any
在其中一个 Promise 完成时给你一个信号。这类似于 Promise.race
,除了 any
在其中一个 Promise 被拒绝时不会提前拒绝。
const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
// Any of the promises was fulfilled.
console.log(first);
// → e.g. 'b'
} catch (error) {
// All of the promises were rejected.
console.assert(error instanceof AggregateError);
// Log the rejection values:
console.log(error.errors);
// → [
// <TypeError: Failed to fetch /endpoint-a>,
// <TypeError: Failed to fetch /endpoint-b>,
// <TypeError: Failed to fetch /endpoint-c>
// ]
}
此代码示例检查哪个端点响应最快,然后记录它。只有当所有请求都失败时,我们才会进入 catch
块,在那里我们可以处理错误。
Promise.any
拒绝可以表示多个错误。为了在语言级别支持这一点,引入了一种新的错误类型,称为 AggregateError
。除了在上面示例中的基本用法之外,AggregateError
对象也可以像其他错误类型一样以编程方式构造。
const aggregateError = new AggregateError([errorA, errorB, errorC], 'Stuff went wrong!');