浅析Promise三兄弟:Promise.all、Promise.race、Promise.allSettled 的理解和使用场景

一、Pomise.all 的使用

  Promise.all 可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。代码示例如下:

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
  resolve('success')
})
let p3 = Promse.reject('失败')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失败了,打出 '失败'
})

  Promse.all 在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。

  需要特别注意的是,Promise.all 获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

二、Promise.race 的使用

  我们先看 MDN 的定义:Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。

  看了是不是一脸懵逼,啥意思,哈哈~~我们再看官方给的例子:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
  // Both resolve, but promise2 is faster
});
// expected output: "two"

  Both resolve, but promise2 is faster ,从这里可以看出端倪。顾名思义,Promse.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

  我们看一下使用 Promise.race –  setTimeout 的示例

var p1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then(function(value) {  // 成功回调
  console.log(value); // "two"
  // 两个都完成,但 p2 更快
});

var p3 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, "three");
});
var p4 = new Promise(function(resolve, reject) {
    setTimeout(reject, 500, "four");
});
Promise.race([p3, p4]).then(function(value) {  // 成功回调
  console.log(value); // "three"
  // p3 更快,所以它完成了
}, function(reason) {   // 失败回调未被调用
  // 未被调用
});

var p5 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, "five");
});
var p6 = new Promise(function(resolve, reject) {
    setTimeout(reject, 100, "six");
});
Promise.race([p5, p6]).then(function(value) {   // 成功回调未被调用
  // 未被调用
}, function(reason) {   // 失败回调
  console.log(reason); // "six"
  // p6 更快,所以它失败了
});

  race 服务比较重要,比如多台服务,想要稳定返回,并且还要快,让他们去竞赛,谁快就用谁。race 的使用场景就是,多台服务器部署了同样的服务端代码,假如我要获取一个商品列表接口,我可以在 race 中写上所有服务器中的查询商品列表的接口地址,哪个服务器响应快,就从哪个服务器拿数据。

三、Promise.allSettled 的使用

  MDN 介绍:Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilledrejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

  当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。

  相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

  看完似乎懂了,但还是有点晦涩的。看示例:

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"

  语法:Promise.allSettled(iterable);

  参数:iterable  -   一个可迭代的对象,例如Array,其中每个成员都是Promise

  返回值:一旦所指定的 promises 集合中每一个 promise 已经完成,无论是成功的达成或被拒绝,未决议的 Promise 将被异步完成。那时,所返回的 promise 的处理器将传入一个数组作为输入,该数组包含原始 promises 集中每个 promise 的结果。

  对于每个结果对象,都有一个 status 字符串。如果它的值为 fulfilled,则结果对象上存在一个 value 。如果值为 rejected,则存在一个 reason 。value(或 reason )反映了每个 promise 决议(或拒绝)的值

  看到这里了解了:它与 Promise.all 比较类似,差别在于对于失败的处理情况不同。Promise.all 是只要有 1 个失败,那么就 reject;而 Promise.allSettled 是不管 Promise List 里的是失败还是成功,都会返回数组,数组里面反映每个 Promise 的信息。

  下面我们再看一下其产生背景和应用:

  ES6引入的Promise极大地提升了我们在写 js 应用的编码体验,我们可以很方便得使用Promise进行异步流程控制,但是有一种情况处理起来其实很是不方便, 这就是Promise.allSettled提案的存在理由。

  举例说明:比如我们用户在页面上面同时填了3干个独立的表单,这三个表单分三个接口提交到后端,三个接口独立,没有顺序依赖,这个时候我们需要等到请求全部完成后给与用户提示表单提交的情况。

  分析:在多个promise 同时进行时,我们很快会想到使用 Promise.all 来进行包装,但是由于 Promise.all 的短路特性,三个提交中若前面任意一个提交失败,则后面的表单也不会进行提交了,这就与我们需求不符合。

  Promise.allSettled 跟 Promise.all 类似,其参数接受一个 Promise 的数组,返回一个新的 Promise,唯一的不同在于:其不会进行短路,也就是说当 Promise 全部处理完成后,我们可以拿到每个 Promise 的状态,而不管其是否处理成功。

  代码示例:

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');
const errors = results
  .filter(p => p.status === 'rejected')
  .map(p => p.reason).join(',');
if(errors){
  // 如果存在错误
  message.error(errors);
}

  然后这里有一篇文章,讲的细一些,还不错,可以学习一下:《Promise 中的三兄弟 .all(), .race(), .allSettled()

posted @ 2021-09-24 18:12  古兰精  阅读(1525)  评论(0编辑  收藏  举报