场景
- 有a、b、c三个异步任务,要求必须先执行a,再执行b,最后执行c
- 且下一次任务必须要拿到上一次任务执行的结果,才能做操作
思路
- 我们需要实现一个队列,将这些异步函数添加进队列并且管理它们的执行,队列具有
First In First Out
的特性,也就是先添加进去的会被先执行,接着才会执行下一个(注意跟栈作区别) - 大家也可以类比一下jQuery的animate方法,添加多个动画也会按顺序执行
解决
-
模拟3个异步函数
// 异步函数a var a = function () { return new Promise(function (resolve, reject) { setTimeout(function () { resolve('a') }, 1000) }) } // 异步函数b var b = function (data) { return new Promise(function (resolve, reject) { resolve(data + 'b') }) } // 异步函数c var c = function (data) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(data + 'c') }, 500) }) }
-
解决方法一(使用then链式操作)
-
特点:可以满足需求,但是书写比较繁琐
-
代码
//链式调用 a() .then(function (data) { return b(data) }) .then(function (data) { return c(data) }) .then(function (data) { console.log(data)// abc })
-
-
方法二(forEach构建队列)
-
特点:封装方法,可移植到别处使用
-
代码
// 构建队列 function queue(arr) { var sequence = Promise.resolve() arr.forEach(function (item) { sequence = sequence.then(item) }) return sequence } // 执行队列 queue([a, b, c]) .then(data => { console.log(data)// abc })
-
-
方法三(reduce构建队列)
-
同方法二,只是显得更装x点
-
代码
function queue(arr) { return arr.reduce((a, b) => { a = a.then(b) return a }, Promise.resolve()) } queue([a, b, c]) .then(data => { console.log(data)// abc })
-
-
方法四(使用async、await构建队列)
-
利用最新的语法糖特性
-
代码
async function queue(arr) { let res = null for (let promise of arr) { res = await promise(res) } return await res } queue([a, b, c]) .then(data => { console.log(data)// abc })
-
-
顺便说一句,bluebird的Promise.reduce也可以用来顺序执行函数,但是可使用的场景非常有限,一般用来读取文件信息,而以上给出的方法,不管你在异步函数中做了什么,只要函数最后返回了一个Promise对象,都可以使用
实战
工作中的异步请求肯定不会像上面的a、b、c这么直白和傻瓜的,都需要我们去抽象封装的
// 模拟复杂封装过的请求
function mockRequest(time) {// time-模拟几秒的请求
return function (data) {// data-上一个请求传来的值
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(time, data, 111);
resolve(data ? data + time : time)// 第一个请求没有接收值,后面的才接收值
}, time * 1000)
})
}
}
function queue(arr) {
return arr.reduce((a, b) => {
a = a.then(b)
return a
}, Promise.resolve())
}
// 执行队列
queue([mockRequest('2'), mockRequest('1'), mockRequest('3')])
.then(data => {
console.log(data, 222)// 213
})