Skip to content

作用域与闭包 - 如何处理循环的异步操作 #30

@logan70

Description

@logan70

如何处理循环的异步操作

示例代码中tasks为异步操作队列,代码忽略任务执行结果的记录和错误处理

并行异步操作

Promise.all

Promise.all(tasks.map(task => task()))
  .then(() => doSomethingAfter())

// 也可结合 async/await使用
;(async () => {
  await Promise.all(tasks.map(task => task()))
  doSomethingAfter()
})()

检测完成个数

let finishedNum = 0
let taskNum = tasks.length
tasks.forEach((task, i) => {
  task(() => {
    // 完成任务个数记录
    finishedNum++

    // 检测是否全部完成
    if (finishedNum === taskNum) {
      doSomethingAfter()
    }
  })
})

串行异步操作

promise.then

使用promise.then循环拼接任务,最后拼接完成之后的操作

let taskPromise = Promise.resolve()
tasks.forEach((task, i) => {
  taskPromise = taskPromise.then(() => task())
})

// 完成所有任务后
taskPromise.then(() => doSomethingAfter())

一个比较抖机灵的写法,通过Array.prototype.reduce实现,原理和上例相同:

const taskPromise = tasks
  .reduce((promise, task) => {
    promise = promise.then(() => task)
    return promise
  }, Promise.resolve())
  .then(() => doSomethingAfter())

递归调用

const runTasksSerially = tasks => {
  if (tasks.length <= 0) {
    return doSomethingAfter()
  }
  const taskToRun = tasks.shift() // 取出首个任务
  // 执行完成后,回调内将剩余任务传入runTasksSerially执行,实现串行
  taskToRun(() => runTasksSerially(tasks))
}

runTasksSerially(tasks)

控制并发数

即将任务按并发数分组,组内并行执行,组间串行执行。

// Promise.all并行执行
const runTasksConcurrently = tasks => Promise.all(tasks.map(task => task()))

// Promise.then串行执行
const runTasksSerially = tasks => tasks.reduce((p, task) => p.then(() => task()), Promise.resolve())

const runTasks = (tasks, concurrency) => {
  // 分割任务组,组个数为 Math.ceil(tasks.length / concurrency),组内任务个数为concurrency
  const dividedTasks = Array.from({ length: Math.ceil(tasks.length / concurrency) }, (_, i) => {
    return tasks.slice(i * concurrency, i * concurrency + concurrency)
  })

  // 生成组内并行执行,组间串行执行的任务组
  const serialTasks = dividedTasks.map(concurrentTasks => {
    return () => runTasksConcurrently(concurrentTasks)
  })

  // 串行执行生成的任务组(注:单个组内的各个任务并行执行)
  return runTasksSerially(serialTasks)
}

runTasks(tasks, 3).then(() => doSomethingAfter())

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions