Blog icon indicating copy to clipboard operation
Blog copied to clipboard

执行机制 - 使用Promise实现串行

Open logan70 opened this issue 5 years ago • 3 comments

使用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()
}

logan70 avatar Dec 12 '19 15:12 logan70

第一种forEach无法实现串行, 因为forEach是并发的

xiahouwei avatar Apr 21 '22 10:04 xiahouwei

@xiahouwei 将task跟在原来的Promise后面,每次都会赋值新的Promise,可以实现串行的

logan70 avatar May 24 '22 12:05 logan70

function iteratorPromise(arr){
  let res = Promise.resolve();
  arr.forEach(fn=>{
    res = res.then(()=>fn()) 
  })
}

iteratorPromise(arr);

jackieli123723 avatar Sep 21 '22 10:09 jackieli123723