Promise 本身不提供任务队列,但可通过链式调用或 async/await 实现串行执行:每个任务必须等待前一个 Promise 完成后才开始;常用 reduce 累积成链或 for...of + await;需避免提前执行导致伪串行。

Promise 本身不直接提供“任务队列”结构,但可以通过链式调用(.then())或递归/循环方式,让异步任务一个接一个地顺序执行——也就是串行执行。关键在于:**每个任务的开始,必须等待前一个 Promise 完成(fulfilled)后再触发**。
用 reduce 串联 Promise 实现串行
这是最常用、最简洁的方式:把任务数组用 reduce 累积成一个长链 Promise。
- 初始值设为
Promise.resolve(),作为链条起点 - 每次把当前任务函数传入
.then(),返回新的 Promise - 这样后一个任务一定等前一个 resolve 后才执行
示例:
const tasks = [
() => delay(1000).then(() => console.log('1')),
() => delay(500).then(() => console.log('2')),
() => delay(800).then(() => console.log('3'))
];
<p>tasks.reduce((chain, task) => chain.then(task), Promise.resolve());
// 输出:1 → 2 → 3,间隔分别为 1s、0.5s、0.8s(串行累加)
用 async/await + for...of 循环更直观
如果任务是函数数组,且你希望代码可读性强、便于调试,async/await 是更自然的选择。
立即学习“Java免费学习笔记(深入)”;
- 定义一个
async函数,在循环中await每个任务 - 每个任务必须返回 Promise(或可被 Promise.resolve 包装的值)
- 循环会自动暂停,直到当前 Promise settle 才继续下一轮
示例:
async function runSerial(tasks) {
for (const task of tasks) {
await task(); // 等待这个 Promise 完成
}
}
<p>runSerial([
() => new Promise(r => setTimeout(() => { console.log('A'); r(); }, 200)),
() => fetch('/api/user').then(res => res.json()).then(u => console.log(u.name)),
() => delay(300).then(() => console.log('C'))
]);
封装成可复用的串行执行工具函数
把逻辑抽出来,支持传入任意函数数组,自动处理错误和返回最终结果。
- 支持任务函数接收前序结果(类似 reduce 的累加器)
- 遇到 reject 自动中断,可选择捕获并继续(按需)
- 返回一个 Promise,resolve 最终结果或 reject 失败原因
简单封装示例:
function serial(tasks) {
return tasks.reduce((prev, curr) =>
prev.then(result => curr(result)),
Promise.resolve()
);
}
<p>// 使用:每个任务函数接收上一个的返回值
serial([
() => Promise.resolve(10),
x => Promise.resolve(x * 2), // 20
x => Promise.reject('oops'), // 中断
x => console.log(x) // 不会执行
]).catch(err => console.error(err)); // 'oops'
注意避免常见陷阱
串行容易写错成“伪串行”,尤其在构造 Promise 时提前执行了任务。
- ❌ 错误:直接写
tasks.map(fn => fn())—— 这会立即并发执行所有函数 - ✅ 正确:确保每个任务是“函数”,不是“已执行的 Promise”,延迟到被调用时才启动
- ⚠️ 注意:Promise 构造器里的代码是同步执行的(如
new Promise(() => { console.log('now') })),要控制好副作用时机 - ? 提示:如果任务本身是纯函数,返回 Promise,就天然适合串行;如果带状态或依赖外部,需额外考虑上下文传递


















