跳到正文

手写练习/异步控制

GitHub

手写请求串行执行

简单异步请求串行

代码实现

JavaScript61 行
/**
 * requestSerial:按顺序依次执行异步任务
 * @param {Function[]} tasks 异步任务函数数组,每一项执行后返回 Promise
 * @returns {Promise<Array>} 所有任务结果,顺序和 tasks 一致
 */

//这里我认为和并发的区别是runNext的调用时机,串行在每个任务完成后才调用,而并发则是同时调用多个任务
function requestSerial(tasks) {
  const results = [];
  let index = 0;

  return new Promise((resolve, reject) => {
    function runNext() {
      if (index === tasks.length) {
        resolve(results);
        return;
      }

      const currentIndex = index;
      const task = tasks[currentIndex];
      index++;

      Promise.resolve()
        .then(() => task())
        .then((result) => {
          results[currentIndex] = result;
          runNext();
        })
        .catch((error) => {
          reject(error);
        });
    }

    runNext();
  });
}

// ==================== 测试
function createTask(id, delay) {
  return () =>
    new Promise((resolve) => {
      setTimeout(() => {
        console.log(`任务 ${id} 完成`);
        resolve(id);
      }, delay);
    });
}

const tasks = [
  createTask(1, 300),
  createTask(2, 100),
  createTask(3, 200),
];

requestSerial(tasks)
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.error(err);
  });

笔记记录

手写请求串行执行

场景

按顺序依次执行异步任务。

常见使用场景:

  • 多个请求之间有先后依赖
  • 需要避免并发请求造成压力
  • 任务必须严格按照顺序执行

题目目标

实现一个 requestSerial 方法:

  • 接收一个异步任务函数数组 tasks
  • 每个任务执行后返回 Promise
  • 前一个任务完成后,才开始执行下一个任务
  • 所有任务成功后,按任务顺序返回结果数组
  • 任意任务失败后,整体失败,并停止后续任务

函数签名

function requestSerial(tasks) {}

核心思路

串行执行的关键是:不要一次性启动所有 Promise。

应该把每个任务包装成函数,等轮到它时再调用:

task();

如果直接传入 Promise 数组,Promise 创建时就已经开始执行,无法保证串行。

可选实现方向

使用 async/await

用 for 循环逐个 await:

  1. 创建 results 数组
  2. 遍历 tasks
  3. await tasks[i]()
  4. 把结果放入 results
  5. 遍历结束后返回 results

使用 reduce

用 Promise 链把任务串起来:

  1. 初始值是 Promise.resolve([])
  2. 每一步等待上一个 Promise 完成
  3. 再执行当前任务
  4. 把结果追加到结果数组中

关键点

  • 任务数组里应该放函数,而不是已经创建好的 Promise。
  • 串行执行不需要并发计数器,只需要保证每一步等待上一步完成。
  • 如果中途失败,后面的任务不应该继续执行。
  • 返回结果顺序天然和执行顺序一致。