手写请求串行执行
代码实现
/**
* 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:
- 创建
results数组 - 遍历
tasks await tasks[i]()- 把结果放入
results - 遍历结束后返回
results
使用 reduce
用 Promise 链把任务串起来:
- 初始值是
Promise.resolve([]) - 每一步等待上一个 Promise 完成
- 再执行当前任务
- 把结果追加到结果数组中
关键点
- 任务数组里应该放函数,而不是已经创建好的 Promise。
- 串行执行不需要并发计数器,只需要保证每一步等待上一步完成。
- 如果中途失败,后面的任务不应该继续执行。
- 返回结果顺序天然和执行顺序一致。