Promise API

78 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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));

实际上,这个方法几乎从未被使用过。