Blog
Blog copied to clipboard
执行机制 - 使用Promise实现串行
使用Promise实现串行
Promise
原型上的then
方法以及Async/Await
基本用法大家都熟悉,不作过多介绍。
下面的实现方法本质上也都是基于以上两种用法的拓展。
普通循环
理论上任何循环函数或语法都可实现。
let promise = Promise.resolve()
function runPromisesSerially(tasks) {
tasks.forEach(task => {
promise = promise.then(task)
})
return promise
}
runPromiseSerially([ task1, task2, ... ])
.then(() => console.log('finished'))
Array.reduce
上面方法通过循环任务数组,不断在promise后使用.then(nextTask)
拼接任务,仔细想想很适合用reduce
来实现:
function runPromisesSerially(tasks) {
return tasks
.reduce((promise, curTask) => promise.then(curTask), Promise.resolve())
}
Async/Await
+ 循环
while
循环也可实现。
async function runPromisesSerially(tasks) {
for (const task of tasks) {
await task()
}
}
递归
function runPromisesSerially([curTask, ...restTasks]) {
const p = Promise.resolve()
if (!curTask) return p
return p.then(curTask).then(() => runPromisesSerially(restTasks))
}
for await of
需要自己实现可异步迭代的对象供for await of
调用。
async function runPromisesSerially([...tasks]) {
const asyncIterable = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
const task = tasks[this.i++]
return task
? task().then(value => ({ done: false, value }))
: Promise.resolve({ done: true })
}
}
}
}
for await (val of asyncIterable) {
// do something
}
}
for await of
+ Async Generator
本质上是异步生成器函数()执行会自动生成异步迭代器,然后异步迭代器可配合for await of
实现串行运行promises
。
async function runPromisesSerially(tasks) {
async function* asyncGenerator() {
let i = 0
while (i < tasks.length) {
const val = await tasks[i]()
i++
yield val
}
}
for await (val of asyncGenerator()) {
// do something
}
}
Generator
Generator本身只是一个状态机,需要通过调用promise.then()
来改变它的状态,实现promises
的串行执行。
function runPromisesSerially(tasks) {
function *gen() {
for (const task of tasks) {
yield task()
}
}
const g = gen()
function next(val) {
const result = g.next(val)
if (result.done) return result.value
result.value.then(val => next(val))
}
next()
}
第一种forEach无法实现串行, 因为forEach是并发的
@xiahouwei 将task跟在原来的Promise后面,每次都会赋值新的Promise,可以实现串行的
function iteratorPromise(arr){
let res = Promise.resolve();
arr.forEach(fn=>{
res = res.then(()=>fn())
})
}
iteratorPromise(arr);