Promise的串行调用

3,959 阅读4分钟

作为一个前端渣渣,昨天面试遇到了一个Promise串行执行的一道面试题,当时没有做出来,今天重新思考了一下,做一个小总结。

题目

先看一下题目,题目的大致意思是,给你一段不完整的代码,进行补充

let arr = [ 
    new Promise(res=>{
        setTimeout(()=>{
            res(1)
        },1000)
​
    }),
    new Promise(res=>{
        setTimeout(()=>{
            res(2)
        },1000)
​
    }), 
    new Promise(res=>{
        setTimeout(()=>{
            res(3)
        },1000)
​
    })]
​
function iteratorPromise(arr){
    //补充代码
}
​
iteratorPromise(arr);

先定义一个Promise的数组,串行执行它们,最后实现如下的输出

Promise.png

看到这道题,首先会想到Promise.all,但是我们知道Promise.all返回的是一个数组的形式,而且就算就这个数组使用...扩展运算符,这三个数组还是再一行显示的,不符合题目的要求。

那么要怎么实现串行显示呢?其实只要将它们用.then链接起来就好了,就是一个上一个Promise.then里面返回下一个Promise;大致长下面这个样子:

Promise1.then((num)=>{
  //...执行操作
  return Promise2.then((num)=>{
    //...执行操作
    return Promise3
  }
})

这样就可以实现第一个Promise执行之后,再执行第二个Promise,依此类推

好了,想到了这里,那实现起来就很简单了,我采用了一个最笨的方法—for遍历[此处附上一个狗头]

function iteratorPromise(arr){
  for(let i = 0;i<arr.length;i++){
     arr[i].then((num)=>{
       console.log(num)
       return arr[i+1]
     })
   }
}

其实也可以用数组的reduce方法,但是reduce再这里会有一个缺点,不能执行最后一个方法的then,也就是说不能打印3,因为它只是返回了Promise3,但是Promise并没有设置它的then来输出;所以我选择了上面的方法

arr.reduce((pre,cur)=>{
    return pre.then((num)=>{
       num && console.log(num)
       return cur
     })
  },Promise.resolve())

到此,这个问题就解决啦!!! 撒花

补充

这里,通过浏览一些博客,发现其实串行还可以用一种更巧妙的方法解决,虽然这种方法并不适用我昨天碰到的面试题,但是也十分值得记录一下:

//首先这个数组的每一项为一个函数,这个函数返回的是一个promise,这也就意味着我们不再需要在then里重新写一个函数啦!
let arr = [()=>{
  return new Promise(res=>{
    setTimeout(()=>{
      console.log("run", Date.now());
      res()
    },1000)
  })
},()=>{
  return new Promise(res=>{
    setTimeout(()=>{
      console.log("run", Date.now());
      res()
    },1000)
    
  })
},()=>{
  return new Promise(res=>{
    setTimeout(()=>{
      console.log("run", Date.now());
      res()
    },1000)
    
  })
}]
​
​
function iteratorPromise(arr){
​
  let res = Promise.resolve();
​
  arr.forEach(fn=>{
    res = res.then(()=>fn()) // 这里其实还是遍历,但是直接返回的是这个数组每一项的函数,通过这个函数来返回每一个Promise
  })
}
​
iteratorPromise(arr);
​

本质是一样的,就是让上一个Promise的then返回下一个Promise,并串成链

知识回顾

我们知道官方是有给我们提供两个同时执行多个Promise的API,那就是allrace

Promise.all

顾名思义,就是执行所有的Promise,但需要注意的是它最后成功的返回结果是以数组的形式将多个Promise的结果进行返回,所有的Promise都成功才会返回成功数组,只要有一个失败,那么会触发失败的回调函数,返回第一个失败Promise的错误信息。

MDN上的例子: Promise.all() - JavaScript | MDN (mozilla.org)

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});
​
Promise.all([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]数组的形式
});

它的使用其实非常的简单就需要我们记住一下几点就可以了:

  • 以数组的形式返回成功的结果
  • 如果参数中包含非 promise 值,这些值将被忽略,但仍然会被放在返回数组中(如果 promise 完成的话)
  • 只要有一个失败,就触发失败回调,返回第一个失败的信息
  • 按照p1、p2、p3,也就是传入promise传入的顺序执行(尽管p1有可能会比p2执行要要慢)

Promise.race

同样,顾名思义,race比赛、赛跑的意思,也就是传入多个promise,哪个最先执行完成,就返回哪个,它不管这个结果是成功还是失败:

MDN上的例子: Promise.race() - JavaScript | MDN (mozilla.org)

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 更快
});

感觉它比all还要简单,不过它使用的场景还是非常少的。

OVER!!!