JavaScript Promise 完全指南
1. 什么是 Promise(JavaScript%20Promise%20完全指南.md#1-什么是-promise) 2. Promise 的三种状态(JavaScript%20Promise%20完全指南.md#2-promise-的三种状态) 3. 创建 Promise(JavaScript%20Promise%20完全指南.md#3-创建-promise) 4. 实例方法(JavaScript%20Promise%20完全指南.md#4-实例方法) - .then()(JavaScript%20Promise%20完全指南.md#41-t
官方文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
涵盖:每个方法、每个参数的含义、基础用法、进阶模式、最佳实践
最后更新:2026-03-05(经官方文档复核)
目录
- 什么是 Promise
- Promise 的三种状态
- 创建 Promise
- 实例方法
- 静态方法
- Promise 链式调用
- 错误处理模式
- async / await 与 Promise 的关系
- 执行顺序与微任务队列
- 常见陷阱
- 最佳实践
- 实战模式
1. 什么是 Promise
Promise 是 JavaScript 中表示异步操作最终结果的对象。它代表一个"承诺":现在不知道结果,但将来某个时刻会知道(成功或失败)。
引入 Promise 的原因:解决回调地狱(Callback Hell)。
// 回调地狱:嵌套难以维护
getUser(id, function(user) {
getOrders(user.id, function(orders) {
getOrderDetail(orders[0].id, function(detail) {
// 越来越深...
}, handleError);
}, handleError);
}, handleError);
// Promise 链:线性、清晰
getUser(id)
.then(user => getOrders(user.id))
.then(orders => getOrderDetail(orders[0].id))
.then(detail => console.log(detail))
.catch(handleError);
2. Promise 的三种状态
| 状态 | 英文 | 含义 | 能否转换 |
|---|---|---|---|
| 等待中 | pending |
初始状态,操作尚未完成 | 可以 → fulfilled 或 rejected |
| 已兑现 | fulfilled |
操作成功完成 | 不可逆 |
| 已拒绝 | rejected |
操作失败 | 不可逆 |
pending ──→ fulfilled
└──→ rejected
关键规则:
- 状态一旦变为
fulfilled或rejected,就永远不会再改变 - 一个 Promise 只能被 resolve 或 reject 一次,后续调用无效
fulfilled和rejected统称为settled(已落定)
const p = new Promise((resolve, reject) => {
resolve('成功'); // 状态变为 fulfilled
reject('失败'); // 这行被忽略,状态已确定
resolve('再次'); // 同样被忽略
});
p.then(v => console.log(v)); // 输出: "成功"
3. 创建 Promise
3.1 构造函数 new Promise(executor)
const promise = new Promise(executor);
参数:
executor — Function
执行器函数,在构造 Promise 时立即同步执行(不是异步的)。
executor(resolve, reject) => void
| 参数 | 类型 | 作用 |
|---|---|---|
resolve |
Function |
调用后将 Promise 状态设为 fulfilled,传入的值作为成功结果 |
reject |
Function |
调用后将 Promise 状态设为 rejected,传入的值作为失败原因 |
const p1 = new Promise((resolve, reject) => {
// executor 是同步执行的
console.log('executor 开始'); // 1. 先打印这里
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('操作成功'); // 将 p1 变为 fulfilled
} else {
reject(new Error('操作失败')); // 将 p1 变为 rejected
}
}, 1000);
console.log('executor 结束'); // 2. 然后打印这里
});
console.log('Promise 已创建'); // 3. 最后打印这里(executor 同步执行)
resolve(value) 的特殊行为
resolve 传入的 value 会影响 Promise 的最终状态:
// 情况1:传入普通值 → 直接 fulfilled
resolve(42);
resolve('hello');
resolve({ data: [] });
// 情况2:传入另一个 Promise → 状态跟随那个 Promise
const inner = new Promise(resolve => setTimeout(() => resolve('inner'), 1000));
resolve(inner); // p1 会等待 inner 完成,然后跟随其状态
// 情况3:传入 thenable 对象(有 .then 方法的对象)→ 跟随 thenable
resolve({
then(resolve, reject) {
resolve('thenable result');
}
});
// 情况4:传入 undefined
resolve(); // fulfilled,值为 undefined
executor 中抛出异常
const p = new Promise((resolve, reject) => {
throw new Error('executor 中的错误');
// 等价于 reject(new Error('executor 中的错误'))
});
p.catch(err => console.log(err.message)); // "executor 中的错误"
// 注意:只有同步抛出才会被捕获!
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error('异步抛出'); // 不会被 Promise 捕获,会变成未处理的异常!
}, 100);
});
4. 实例方法
4.1 .then()
.then() 是 Promise 最核心的方法,用于注册成功和失败的回调。
promise.then(onFulfilled, onRejected)
参数:
onFulfilled — Function | null | undefined
Promise 变为 fulfilled 时调用的函数。
onFulfilled(value) => any
| 内容 | 说明 |
|---|---|
参数 value |
Promise resolve 时传入的值 |
| 返回值 | 决定 .then() 返回的新 Promise 的状态(见下方详解) |
| 若传入非函数 | 会被忽略,值透传到下一个 .then() |
onRejected — Function | null | undefined
Promise 变为 rejected 时调用的函数。
onRejected(reason) => any
| 内容 | 说明 |
|---|---|
参数 reason |
Promise reject 时传入的值(通常是 Error 对象) |
| 返回值 | 同样决定新 Promise 的状态 |
| 若传入非函数 | 会被忽略,拒绝原因透传到下一个 .catch() |
返回值规则(重要!)
.then() 总是返回一个新的 Promise,新 Promise 的状态取决于回调函数的返回值:
const p = Promise.resolve(1);
// 规则1:回调返回普通值 → 新 Promise fulfilled,值为该返回值
p.then(v => v + 1) // fulfilled(2)
.then(v => console.log(v)); // 输出: 2
// 规则2:回调返回 Promise → 新 Promise 跟随该 Promise
p.then(v => Promise.resolve(v * 10)) // 返回的 Promise fulfilled(10)
.then(v => console.log(v)); // 输出: 10
p.then(v => Promise.reject('error')) // 返回的 Promise rejected('error')
.catch(e => console.log(e)); // 输出: "error"
// 规则3:回调中抛出异常 → 新 Promise rejected,reason 为该异常
p.then(v => { throw new Error('oops'); })
.catch(e => console.log(e.message)); // 输出: "oops"
// 规则4:回调返回 undefined(没有 return 语句)→ fulfilled(undefined)
p.then(v => { console.log(v); }) // 没有返回值
.then(v => console.log(v)); // 输出: undefined
// 规则5:onFulfilled 是非函数 → 值透传
p.then(null) // 透传
.then(null) // 透传
.then(v => console.log(v)); // 输出: 1
完整示例
fetch('/api/user')
.then(
// onFulfilled:请求成功
(response) => {
console.log('状态码:', response.status);
return response.json(); // 返回新 Promise
},
// onRejected:网络错误(不推荐在这里处理,推荐用 .catch())
(networkError) => {
console.error('网络错误:', networkError);
throw networkError; // 重新抛出,继续传递
}
)
.then(data => {
console.log('用户数据:', data);
});
4.2 .catch()
.catch() 是 .then(null, onRejected) 的语法糖,专门用于捕获错误。
promise.catch(onRejected)
// 完全等价于
promise.then(undefined, onRejected)
参数:
onRejected — Function
onRejected(reason) => any
| 内容 | 说明 |
|---|---|
参数 reason |
拒绝原因,通常是 Error 实例 |
| 返回值 | 决定 .catch() 返回的新 Promise 的状态 |
关键:.catch() 返回的也是新 Promise,可以继续链式调用。
// 错误恢复:在 catch 中返回值,可以从错误中"恢复"
Promise.reject(new Error('数据库连接失败'))
.catch(err => {
console.log('错误:', err.message);
return '使用缓存数据'; // 返回备用值,链式继续为 fulfilled 状态
})
.then(data => {
console.log('数据:', data); // 输出: "使用缓存数据"
});
// 错误传递:在 catch 中继续抛出,传递给下一个 catch
Promise.reject(new Error('原始错误'))
.catch(err => {
console.log('第一个 catch:', err.message);
throw new Error('包装后的错误'); // 继续传递
})
.catch(err => {
console.log('第二个 catch:', err.message); // 捕获包装后的错误
});
为何推荐用 .catch() 而不是 .then() 的第二个参数:
// 问题:.then(onFulfilled, onRejected) 中,onRejected 无法捕获 onFulfilled 中的错误
promise
.then(
data => { throw new Error('onFulfilled 中出错'); }, // 这个错误...
err => console.log('这里捕获不到上面的错误') // ...不会到这里
);
// 推荐:.catch() 放在链末尾,可以捕获整条链中的所有错误
promise
.then(data => { throw new Error('任何地方的错误'); })
.then(data => { /* ... */ })
.catch(err => console.log('统一捕获:', err.message)); // 能捕获上面任意一步的错误
4.3 .finally()
无论 Promise 成功还是失败,都会执行的回调。ES2018 引入。
promise.finally(onFinally)
参数:
onFinally — Function
onFinally() => any
| 内容 | 说明 |
|---|---|
| 参数 | 无参数(无法获知 Promise 的结果或原因) |
| 返回值(普通值) | 被忽略,原 Promise 的值或原因会透传 |
| 返回值(Promise) | 等待该 Promise,若它 rejected 则覆盖原来的状态 |
// 透传行为演示
Promise.resolve('成功的值')
.finally(() => {
console.log('finally 执行了');
return '这个返回值会被忽略'; // 被忽略
})
.then(v => console.log(v)); // 输出: "成功的值"(透传)
Promise.reject(new Error('失败原因'))
.finally(() => {
console.log('finally 也执行了');
// 不 return 任何东西
})
.catch(e => console.log(e.message)); // 输出: "失败原因"(透传)
// finally 返回 rejected Promise 会覆盖原状态
Promise.resolve('原来的成功值')
.finally(() => Promise.reject(new Error('finally 中的错误')))
.catch(e => console.log(e.message)); // 输出: "finally 中的错误"
典型用途:资源清理
let connection;
getDbConnection()
.then(conn => {
connection = conn;
return conn.query('SELECT * FROM users');
})
.then(users => {
console.log(users);
})
.catch(err => {
console.error('查询失败:', err);
})
.finally(() => {
// 无论成功还是失败,都关闭连接
if (connection) connection.close();
});
5. 静态方法
5.1 Promise.resolve()
创建一个立即 fulfilled 的 Promise。
Promise.resolve(value)
参数:
value — any
value 类型 |
返回结果 |
|---|---|
| 普通值(数字、字符串、对象等) | 返回 fulfilled(value) 的 Promise |
| 另一个 Promise 实例 | 直接返回该 Promise 本身(不包装) |
| Thenable 对象 | 返回跟随该 thenable 状态的 Promise |
无参数 / undefined |
返回 fulfilled(undefined) |
// 普通值
Promise.resolve(42).then(v => console.log(v)); // 42
Promise.resolve('hello').then(v => console.log(v)); // "hello"
// 传入 Promise → 返回同一个对象
const original = new Promise(resolve => resolve(1));
const wrapped = Promise.resolve(original);
console.log(original === wrapped); // true!不会创建新 Promise
// 传入 thenable
Promise.resolve({
then(resolve) { resolve('thenable value'); }
}).then(v => console.log(v)); // "thenable value"
// 常见用途:将同步值包装为 Promise,统一异步接口
function getData(id) {
if (cache.has(id)) {
return Promise.resolve(cache.get(id)); // 统一返回 Promise
}
return fetch(`/api/data/${id}`).then(r => r.json());
}
5.2 Promise.reject()
创建一个立即 rejected 的 Promise。
Promise.reject(reason)
参数:
reason — any
拒绝原因,强烈建议传入 Error 实例(保留调用栈信息)。
// 推荐:传入 Error 实例
Promise.reject(new Error('出错了'))
.catch(err => console.log(err.stack)); // 包含完整调用栈
// 不推荐:传入字符串(没有调用栈)
Promise.reject('出错了')
.catch(reason => console.log(reason)); // 只有字符串
// 注意:与 Promise.resolve 不同,传入 Promise 不会跟随,而是直接作为 reason
const inner = Promise.resolve('inner');
Promise.reject(inner)
.catch(reason => {
console.log(reason === inner); // true,inner 被当作 reason,不会解包
});
5.3 Promise.all()
等待所有 Promise 成功,任意一个失败则立即失败。
Promise.all(iterable)
参数:
iterable — Iterable
包含 Promise(或任意值)的可迭代对象(通常是数组)。
- 非 Promise 值会被
Promise.resolve()包装 - 空数组
[]返回fulfilled([])
返回值:新 Promise
| 情况 | 结果 |
|---|---|
| 所有 Promise 成功 | fulfilled([result1, result2, ...]) — 结果数组顺序与输入一致 |
| 任意一个 Promise 失败 | 立即 rejected(reason) — 第一个失败的原因,其余 Promise 继续执行但结果被忽略 |
| iterable 为空 | fulfilled([]) |
// 基础用法:并发请求多个接口
const [user, orders, messages] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/orders').then(r => r.json()),
fetch('/api/messages').then(r => r.json()),
]);
// 结果顺序与输入顺序一致,与完成时间无关
Promise.all([
new Promise(r => setTimeout(() => r('慢'), 1000)),
new Promise(r => setTimeout(() => r('快'), 100)),
]).then(([slow, fast]) => {
console.log(slow); // "慢"(第一个输入,虽然后完成)
console.log(fast); // "快"(第二个输入)
});
// 任意失败则整体失败
Promise.all([
Promise.resolve(1),
Promise.reject(new Error('第二个失败了')),
Promise.resolve(3), // 这个成功了,但结果被忽略
]).catch(err => console.log(err.message)); // "第二个失败了"
// 混合普通值
Promise.all([1, Promise.resolve(2), 'three'])
.then(results => console.log(results)); // [1, 2, "three"]
5.4 Promise.allSettled()
等待所有 Promise 落定(无论成功还是失败)。ES2020 引入。
Promise.allSettled(iterable)
参数:同 Promise.all()。
返回值:fulfilled(results) — 永远不会 reject。
results 是对象数组,每个对象的格式:
// 成功时
{ status: 'fulfilled', value: any }
// 失败时
{ status: 'rejected', reason: any }
const results = await Promise.allSettled([
Promise.resolve('成功1'),
Promise.reject(new Error('失败')),
Promise.resolve('成功2'),
]);
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason.message);
}
});
// 成功: 成功1
// 失败: 失败
// 成功: 成功2
// 与 Promise.all 的对比:
// Promise.all → 任意失败就整体失败(快速失败)
// Promise.allSettled → 等全部完成再汇总(全量结果)
适用场景:批量操作中需要知道每个操作的结果。
// 批量上传文件,统计成功/失败数量
const uploadResults = await Promise.allSettled(
files.map(file => uploadFile(file))
);
const succeeded = uploadResults.filter(r => r.status === 'fulfilled').length;
const failed = uploadResults.filter(r => r.status === 'rejected').length;
console.log(`上传完成:${succeeded} 成功,${failed} 失败`);
5.5 Promise.any()
返回第一个成功的 Promise,全部失败才失败。ES2021 引入。
Promise.any(iterable)
参数:同 Promise.all()。
返回值:新 Promise
| 情况 | 结果 |
|---|---|
| 任意一个成功 | 立即 fulfilled(value) — 第一个成功的值 |
| 全部失败 | rejected(AggregateError) — 包含所有失败原因 |
| iterable 为空 | rejected(AggregateError) |
// 基础用法:多个数据源,取最快成功的那个
const data = await Promise.any([
fetch('https://cdn1.example.com/data').then(r => r.json()),
fetch('https://cdn2.example.com/data').then(r => r.json()),
fetch('https://cdn3.example.com/data').then(r => r.json()),
]); // 哪个 CDN 最快返回就用哪个
// 全部失败时的错误
try {
await Promise.any([
Promise.reject(new Error('失败1')),
Promise.reject(new Error('失败2')),
]);
} catch (err) {
console.log(err instanceof AggregateError); // true
console.log(err.message); // "All promises were rejected"
console.log(err.errors); // [Error: 失败1, Error: 失败2]
}
5.6 Promise.race()
返回第一个落定(无论成功还是失败)的 Promise 的结果。
Promise.race(iterable)
参数:同 Promise.all()。
返回值:新 Promise,状态与第一个落定的 Promise 相同。
| 情况 | 结果 |
|---|---|
| 第一个落定是 fulfilled | fulfilled(value) |
| 第一个落定是 rejected | rejected(reason) |
| iterable 为空 | 永远 pending(!) |
// 超时控制:最经典的用法
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error(`超时 ${ms}ms`)), ms)
);
return Promise.race([promise, timeout]);
}
try {
const result = await withTimeout(fetch('/api/slow'), 3000);
} catch (err) {
console.log(err.message); // 如果超时: "超时 3000ms"
}
// 注意:race 不会取消"落败"的 Promise,它们仍在后台执行
// 要真正取消,需要使用 AbortController
四个组合方法对比:
| 方法 | 成功条件 | 失败条件 | 返回 | 引入版本 |
|---|---|---|---|---|
Promise.all |
全部成功 | 任意失败(快速失败) | 所有结果数组 | ES2015 |
Promise.allSettled |
全部落定 | 永不失败 | 所有状态对象数组 | ES2020 |
Promise.any |
任意成功 | 全部失败 | 第一个成功值 | ES2021 |
Promise.race |
第一个落定成功 | 第一个落定失败 | 第一个落定值 | ES2015 |
Promise.try |
回调正常返回 | 回调抛出异常 | 包装回调结果 | ES2026 |
5.7 Promise.withResolvers()
返回一个包含 Promise 及其 resolve/reject 函数的对象,让你在 Promise 外部控制其状态。ES2024 引入。
Promise.withResolvers()
返回值:{ promise, resolve, reject }
// 传统写法:需要在 executor 内部保存引用
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
// withResolvers 写法:更简洁
const { promise, resolve, reject } = Promise.withResolvers();
// 在任意地方控制 Promise
document.getElementById('btn').addEventListener('click', () => {
resolve('用户点击了按钮');
});
const result = await promise;
console.log(result);
5.8 Promise.try()
将任意类型的回调(同步或异步、返回值或抛出异常)统一包装成 Promise。ES2026 引入(Baseline 2025)。
Promise.try(func)
Promise.try(func, arg1, arg2, ...argN)
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
func |
Function |
同步调用的回调函数,可以返回值、抛出异常或返回 Promise |
arg1...argN |
any |
传给 func 的参数列表 |
返回值:新 Promise
- 若
func同步返回值 → 已 fulfilled 的 Promise - 若
func同步抛出异常 → 已 rejected 的 Promise - 若
func返回 Promise → 跟随该 Promise
// 统一处理同步和异步回调
function doSomething(action) {
return Promise.try(action)
.then(result => console.log(result))
.catch(err => console.error(err))
.finally(() => console.log('完成'));
}
doSomething(() => '同步结果'); // 同步返回值
doSomething(() => { throw new Error('同步错误'); }); // 同步抛出
doSomething(async () => '异步结果'); // 返回 Promise
// 对比旧写法:
// Promise.resolve(func()) 不能捕获同步抛出
// new Promise(resolve => resolve(func())) 才等价,但更繁琐
典型用途:消除回调是同步还是异步的不确定性,统一用 Promise 链处理。
6. Promise 链式调用
6.1 链式调用的工作原理
每个 .then() / .catch() / .finally() 都返回一个新的 Promise,因此可以无限链接。
Promise.resolve(1)
.then(v => v + 1) // 返回 fulfilled(2)
.then(v => v * 3) // 返回 fulfilled(6)
.then(v => {
if (v > 5) throw new Error('太大了');
}) // 返回 rejected(Error)
.catch(err => {
console.log(err.message); // "太大了"
return 0; // 从错误中恢复,返回 fulfilled(0)
})
.then(v => console.log(v)); // 0
6.2 常见错误:不返回内层 Promise
// 错误:没有 return,导致链断裂
fetch('/api/user')
.then(response => {
response.json().then(data => { // 这里没有 return!
console.log(data);
});
// 这个 .then 回调没有明确返回,
// 所以下一个 .then 不会等待 response.json() 完成
})
.then(() => {
// 这里可能在 data 被打印之前就执行了
});
// 正确:return 内层 Promise
fetch('/api/user')
.then(response => {
return response.json(); // 必须 return!
})
.then(data => {
console.log(data); // 保证在数据解析后执行
});
7. 错误处理模式
7.1 链末捕获(推荐)
doStep1()
.then(doStep2)
.then(doStep3)
.catch(err => {
// 捕获以上任意一步的错误
console.error('某一步出错了:', err);
});
7.2 局部捕获(错误不扩散)
doStep1()
.then(result1 =>
doStep2(result1).catch(err => {
// 只捕获 step2 的错误,提供默认值
console.warn('step2 失败,使用默认值');
return defaultValue; // 继续链式
})
)
.then(doStep3)
.catch(err => {
// 只有 step1 和 step3 的错误到达这里
});
7.3 错误转换
function getUser(id) {
return fetch(`/api/users/${id}`)
.then(response => {
if (!response.ok) {
// 将 HTTP 错误转换为有意义的自定义错误
throw new UserNotFoundError(`用户 ${id} 不存在,状态码: ${response.status}`);
}
return response.json();
});
}
7.4 未处理的 Promise 拒绝
// 在 Node.js 中监听未处理的 rejection
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的 Promise 拒绝:', reason);
});
// 在浏览器中
window.addEventListener('unhandledrejection', event => {
console.error('未处理的 Promise 拒绝:', event.reason);
event.preventDefault(); // 阻止默认的控制台错误
});
8. async / await 与 Promise 的关系
async/await 是 Promise 的语法糖,底层仍然是 Promise。
8.1 对应关系
// Promise 写法
function fetchUser(id) {
return fetch(`/api/users/${id}`)
.then(res => res.json())
.then(user => {
console.log(user.name);
return user;
})
.catch(err => {
console.error(err);
throw err;
});
}
// async/await 写法(等价)
async function fetchUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
const user = await res.json();
console.log(user.name);
return user; // async 函数自动包装为 Promise.resolve(user)
} catch (err) {
console.error(err);
throw err; // async 函数中的 throw 等价于 Promise.reject(err)
}
}
8.2 async 函数的返回值
async function f1() { return 42; }
// 等价于
function f1() { return Promise.resolve(42); }
async function f2() { throw new Error('err'); }
// 等价于
function f2() { return Promise.reject(new Error('err')); }
async function f3() { return Promise.resolve(42); }
// 返回 fulfilled(42),不会双重包装
8.3 并发控制
// 错误:串行执行(每个 await 等待上一个完成)
async function serial() {
const a = await fetch('/api/a'); // 先等这个
const b = await fetch('/api/b'); // 再等这个(总时间 = a + b)
return [a, b];
}
// 正确:并发执行
async function concurrent() {
const [a, b] = await Promise.all([
fetch('/api/a'),
fetch('/api/b'),
]); // 总时间 = max(a, b)
return [a, b];
}
// 先触发,后等待
async function concurrent2() {
const promiseA = fetch('/api/a'); // 立即触发
const promiseB = fetch('/api/b'); // 立即触发(不加 await)
const a = await promiseA; // 然后等待
const b = await promiseB;
return [a, b];
}
9. 执行顺序与微任务队列
Promise 的回调作为微任务(Microtask)执行,优先于宏任务(Macrotask)。
console.log('1. 同步代码 开始');
setTimeout(() => console.log('5. 宏任务 (setTimeout)'), 0);
Promise.resolve()
.then(() => console.log('3. 微任务 1'))
.then(() => console.log('4. 微任务 2'));
console.log('2. 同步代码 结束');
// 输出顺序:
// 1. 同步代码 开始
// 2. 同步代码 结束
// 3. 微任务 1
// 4. 微任务 2
// 5. 宏任务 (setTimeout)
执行模型
同步代码(调用栈)
↓ 清空
微任务队列(Promise.then/catch/finally)
↓ 全部清空
宏任务队列(setTimeout, setInterval, I/O...)
↓ 取一个执行
微任务队列(处理本轮宏任务产生的微任务)
↓ 全部清空
...
// 嵌套的微任务
Promise.resolve().then(() => {
console.log('微任务 A');
Promise.resolve().then(() => {
console.log('微任务 A 的子任务'); // 在 B 之前!
});
}).then(() => {
console.log('微任务 B');
});
// 输出:
// 微任务 A
// 微任务 A 的子任务
// 微任务 B
10. 常见陷阱
陷阱1:在循环中使用 Promise
const ids = [1, 2, 3, 4, 5];
// 错误:forEach 不等待 async 回调
ids.forEach(async (id) => {
const data = await fetchData(id); // forEach 不 await 这个
process(data);
});
// 以上代码立即结束,所有请求"发出"但不等待
// 正确:使用 Promise.all(并发)
await Promise.all(ids.map(id => fetchData(id).then(process)));
// 正确:使用 for...of(串行,保证顺序)
for (const id of ids) {
const data = await fetchData(id);
process(data);
}
陷阱2:Promise 构造函数中的异步 throw
// 错误:异步抛出不会被 Promise 捕获
const p = new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error('异步错误'); // 未捕获异常!会崩溃
}, 100);
});
// 正确:必须调用 reject
const p = new Promise((resolve, reject) => {
setTimeout(() => {
try {
// ...
} catch (err) {
reject(err); // 必须手动 reject
}
}, 100);
});
陷阱3:忘记 return
// 陷阱:两个独立的 Promise 链,不是一个链
function processData() {
fetch('/api/data')
.then(r => r.json())
.then(data => save(data)); // 这里忘记 return
// 函数返回 undefined,调用方无法知道何时完成
}
// 正确
function processData() {
return fetch('/api/data') // return!
.then(r => r.json())
.then(data => save(data));
}
性能陷阱4:不必要的 new Promise 包装
// 反模式:Promise 构造函数反模式(Explicit Promise Construction Anti-Pattern)
function getUser(id) {
return new Promise((resolve, reject) => {
fetch(`/api/users/${id}`) // fetch 本身已经返回 Promise!
.then(r => r.json())
.then(resolve) // 多此一举
.catch(reject); // 多此一举
});
}
// 正确:直接返回 Promise 链
function getUser(id) {
return fetch(`/api/users/${id}`).then(r => r.json());
}
陷阱5:吞掉错误
// 危险:空的 catch 把错误吞掉
promise.catch(() => {}); // 错误消失,无法调试
// 至少记录一下
promise.catch(err => console.error('Promise 错误:', err));
11. 最佳实践
1. 始终处理拒绝
// 每个 Promise 链都应该有错误处理
somePromise()
.then(handleSuccess)
.catch(handleError); // 不能省
2. 避免嵌套,保持链式
// 避免嵌套 Promise
// 坏
p1.then(r1 => {
p2(r1).then(r2 => {
p3(r2).then(r3 => {
// 嵌套地狱回归了
});
});
});
// 好:平铺链式
p1.then(r1 => p2(r1))
.then(r2 => p3(r2))
.then(r3 => { /* 处理 r3 */ })
.catch(handleError);
3. reject 时传入 Error 对象
// 坏
reject('something went wrong');
reject({ code: 500, message: 'error' });
// 好:保留调用栈,便于调试
reject(new Error('something went wrong'));
reject(Object.assign(new Error('error'), { code: 500 }));
4. 用 Promise.all 做并发,而非串行
// 独立的异步操作应该并发
const [user, config] = await Promise.all([getUser(), getConfig()]);
5. 使用 AbortController 取消请求
const controller = new AbortController();
const promise = fetch('/api/data', {
signal: controller.signal,
});
// 取消
setTimeout(() => controller.abort(), 3000);
try {
const data = await promise;
} catch (err) {
if (err.name === 'AbortError') {
console.log('请求被取消');
}
}
6. 注意 finally 的透传特性
// finally 中的普通返回值被忽略,但不要依赖这一"隐式"行为
// 如果在 finally 中需要修改结果,使用 then
// 如果 finally 中有异步操作,必须 return Promise
.finally(async () => {
await cleanup(); // 必须 return 才等待!
})
// 应改为:
.finally(() => cleanup()) // 返回 cleanup() 的 Promise
12. 实战模式
12.1 重试机制
/**
* 带重试的 Promise 执行器
* @param {Function} fn - 返回 Promise 的函数
* @param {number} retries - 最大重试次数
* @param {number} delay - 重试间隔(ms)
*/
function retry(fn, retries = 3, delay = 1000) {
return fn().catch(err => {
if (retries <= 0) throw err;
console.log(`失败,${delay}ms 后重试,剩余次数: ${retries}`);
return new Promise(resolve => setTimeout(resolve, delay))
.then(() => retry(fn, retries - 1, delay * 2)); // 指数退避
});
}
// 使用
const data = await retry(() => fetch('/api/unstable').then(r => r.json()));
12.2 并发限制
/**
* 限制并发数量的 Promise 执行器
* @param {Array} tasks - 任务数组(每个元素是返回 Promise 的函数)
* @param {number} concurrency - 最大并发数
*/
async function limitConcurrency(tasks, concurrency) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const promise = task().then(result => {
executing.delete(promise);
return result;
});
executing.add(promise);
results.push(promise);
if (executing.size >= concurrency) {
await Promise.race(executing); // 等待最快完成的那个
}
}
return Promise.all(results);
}
// 使用:同时最多 3 个请求
const results = await limitConcurrency(
urls.map(url => () => fetch(url).then(r => r.json())),
3
);
12.3 超时 + 取消
function fetchWithTimeout(url, ms = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), ms);
return fetch(url, { signal: controller.signal })
.finally(() => clearTimeout(timeoutId)); // 成功后清除定时器
}
12.4 缓存 Promise(防止重复请求)
const cache = new Map();
function cachedFetch(url) {
if (cache.has(url)) {
return cache.get(url); // 返回同一个 Promise(即使还没完成)
}
const promise = fetch(url)
.then(r => r.json())
.catch(err => {
cache.delete(url); // 失败时清除缓存,允许重试
throw err;
});
cache.set(url, promise);
return promise;
}
// 即使短时间内被调用多次,也只发出一个 HTTP 请求
cachedFetch('/api/config');
cachedFetch('/api/config'); // 返回同一个 Promise
12.5 事件转 Promise
// 将事件监听器转换为 Promise
function waitForEvent(emitter, event, errorEvent = 'error') {
return new Promise((resolve, reject) => {
const onSuccess = (data) => {
emitter.removeListener(errorEvent, onError);
resolve(data);
};
const onError = (err) => {
emitter.removeListener(event, onSuccess);
reject(err);
};
emitter.once(event, onSuccess);
emitter.once(errorEvent, onError);
});
}
// 使用
const data = await waitForEvent(stream, 'data');
await waitForEvent(connection, 'open');
12.6 顺序执行任务队列
// 将一组异步任务按顺序执行,并收集结果
function sequentialPromises(tasks) {
return tasks.reduce(
(chain, task) => chain.then(results =>
task().then(result => [...results, result])
),
Promise.resolve([])
);
}
const results = await sequentialPromises([
() => fetch('/api/step1').then(r => r.json()),
() => fetch('/api/step2').then(r => r.json()),
() => fetch('/api/step3').then(r => r.json()),
]);
快速参考卡
创建
new Promise((resolve, reject) => {...}) 手动创建
Promise.resolve(value) 立即成功
Promise.reject(reason) 立即失败
Promise.withResolvers() 外部控制
实例方法
.then(onFulfilled, onRejected?) 成功/失败回调
.catch(onRejected) 等价 .then(null, fn)
.finally(onFinally) 无论如何都执行
组合方法
Promise.all([...]) 全成功 / 一失败立即失败
Promise.allSettled([...]) 全落定 / 永不失败
Promise.any([...]) 一成功 / 全失败才失败
Promise.race([...]) 第一个落定(成功或失败)
Promise.try(fn, ...args) 包装任意回调为 Promise(ES2026)
最佳实践
优先用 async/await 而非 .then 链:async/await 使控制流更线性,错误处理用 try/catch 比 .catch 更直观,尤其是多个顺序异步操作时。
并发请求用 Promise.allSettled 代替 Promise.all:Promise.all 中任一失败即短路,适合"全部成功才有意义"的场景;Promise.allSettled 等所有请求完成后检查每个结果,适合批量操作需要汇总失败列表的场景。
Promise 链末尾始终加 .catch:未捕获的 Promise 拒绝在 Node.js 中会导致进程崩溃(--unhandled-rejections=throw),在浏览器中触发 unhandledrejection 事件。
用 Promise.race 实现超时控制:
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 5000)
);
const result = await Promise.race([fetch(url), timeout]);
避免在循环中顺序 await:for 循环中 await 会逐个等待,所有请求串行执行;改用 Promise.all 并发:
// 差:串行,总耗时 = sum(各请求时间)
for (const url of urls) await fetch(url);
// 好:并发,总耗时 ≈ max(各请求时间)
await Promise.all(urls.map(url => fetch(url)));
常见陷阱
陷阱:forEach 中 await 不等待内部 Promise
现象: arr.forEach(async item => { await process(item) }) 执行后数据没有处理完,函数已经返回。
原因: forEach 不处理回调的返回值(Promise),内部 await 只在回调协程内暂停,外层 forEach 继续执行。
解决: 改用 for...of 或 Promise.all(arr.map(async item => { await process(item) }))。
陷阱:async 函数总返回 Promise,普通 return 值也被包裹
现象: const val = myAsyncFn() 以为得到返回值,实际是 Promise 对象。
原因: async 函数的返回值自动包在 Promise.resolve() 中,调用方必须 await。
解决: 调用 async 函数时始终 await,或用 .then() 处理结果。
陷阱:Promise 构造函数中同步抛出的错误不被捕获
现象: new Promise(() => { throw new Error('sync') }) 中抛出的错误没有被外部 .catch 捕获。
原因: Promise 构造函数的 executor 中同步抛出会被转换为拒绝,.catch 可以捕获;但若在 setTimeout 等异步回调中抛出,则脱离 Promise 链,变为未捕获异常。
解决: 在 executor 的异步回调中用 reject(error) 而非 throw。