作为一个前端渣渣,昨天面试遇到了一个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.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,那就是all和race
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!!!