持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情
前言
- 前面说了promise的一些简单实用,今天来看看它的6种静态方法
一、Promise.all
语法:
let promise = Promise.all(iterable)
小例子
例如,下面的 Promise.all 在 3 秒之后 settled,然后它的结果就是一个 [1, 2, 3] 数组:
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(alert); // 1,2,3 当上面这些 promise 准备好时:每个 promise 都贡献了数组中的一个元素
数组输出的顺序只于初始顺序一致,即使第一个promise花费时间很长,顺序依旧不变
-
Promise.all的作用:接收一个可迭代(数组之类的),返回一个新的promise。只有所有原始promise都resolve才能有新的promise结果
-
通常,
Promise.all(...)接受含有 promise 项的可迭代对象(大多数情况下是数组)作为参数。但是,如果这些对象中的任何一个不是 promise,那么它将被“按原样”传递给结果数组。
例如,这里的结果是 [1, 2, 3]:
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
}),
2,
3
]).then(alert); // 1, 2, 3
所以我们可以在方便的地方将准备好的值传递给 Promise.all。
如果任意一个 promise 被 reject,由 Promise.all 返回的 promise 就会立即 reject,并且带有的就是这个 error。
例如:
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Whoops!
这里的第二个 promise 在两秒后 reject。这立即导致了 Promise.all 的 reject,因此 .catch 执行了:被 reject 的 error 成为了整个 Promise.all 的结果。
简单的应用
一个常见的技巧是,将一个任务数据数组映射(map)到一个 promise 数组,然后将其包装到 Promise.all。
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
// 将每个 url 映射(map)到 fetch 的 promise 中
let requests = urls.map(url => fetch(url));
// Promise.all 等待所有任务都 resolved
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));
二、Promise.allSettled
- 前面提到的promise.all需要全部都resolve才可,但有些时候我们想得到所有的结果。例如,我们想要获取(fetch)多个用户的信息。即使其中一个请求失败,我们仍然对其他的感兴趣。
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://no-such-url'
];
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => { // (*)
results.forEach((result, num) => {
if (result.status == "fulfilled") {
alert(`${urls[num]}: ${result.value.status}`);
}
if (result.status == "rejected") {
alert(`${urls[num]}: ${result.reason}`);
}
});
});
Promise.allSettled 等待所有的 promise 都被 settle,无论结果如何。结果数组具有:
{status:"fulfilled", value:result}对于成功的响应,{status:"rejected", reason:error}对于 error。
上面的 (*) 行中的 results 将会是:
[
{status: 'fulfilled', value: ...response...},
{status: 'fulfilled', value: ...response...},
{status: 'rejected', reason: ...error object...}
]
所以,对于每个 promise,我们都得到了其状态(status)和 value/reason。
三、 Promise.race
- 与Promise.all不同之处是只等待第一个 settled 的 promise 并获取其结果(或 error,顺序取决于速度
例如,这里的结果将是 1:
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1
这里第一个 promise 最快,所以它变成了结果。第一个 settled 的 promise “赢得了比赛”之后,所有进一步的 result/error 都会被忽略。
四、promise.any
- 它只等待第一个
fulfilled的promise将其返回,若所有promise都是rejected就返回带有AggregateError—— 一个特殊的 error 对象,在其errors属性中存储着所有 promise error。
情况一
Promise.any([
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 1000)),
new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1
这里的第一个 promise 是最快的,但 rejected 了,所以第二个 promise 则成为了结果。在第一个 fulfilled /resolved的 promise “赢得比赛”后,所有进一步的结果都将被忽略。
情况二
Promise.any([
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ouch!")), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Error!")), 2000))
]).catch(error => {
console.log(error.constructor.name); // AggregateError
console.log(error.errors[0]); // Error: Ouch!
console.log(error.errors[1]); // Error: Error!
});
正如你所看到的,我们在 AggregateError 错误类型的 error 实例的 errors 属性中可以访问到失败的 promise 的 error 对象。
五、Promise.resolve
Promise.resolve(value)用结果value创建一个 resolved 的 promise。
下面的 loadCached 函数获取(fetch)一个 URL 并记住其内容。以便将来对使用相同 URL 的调用,它能立即从缓存中获取先前的内容,但使用 Promise.resolve 创建了一个该内容的 promise,所以返回的值始终是一个 promise。
let cache = new Map();
function loadCached(url) {
if (cache.has(url)) {
return Promise.resolve(cache.get(url)); // (*)
}
return fetch(url)
.then(response => response.text())
.then(text => {
cache.set(url,text);
return text;
});
}
我们可以使用 loadCached(url).then(…),因为该函数保证了会返回一个 promise。我们就可以放心地在 loadCached 后面使用 .then。这就是 (*) 行中 Promise.resolve 的目的。
六、Promise.reject
Promise.reject(error) 用 error 创建一个 rejected 的 promise。
如同:
let promise = new Promise((resolve, reject) => reject(error));
实际上,这个方法几乎从未被使用过。