Node.js Promise与async/await深入
深入理解 Promise 和 async/await 的内部机制,写出更高效的异步代码。
Promise 状态机
JavaScript
// Promise 三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 状态转换规则:
// pending → fulfilled(不可逆)
// pending → rejected(不可逆)
// 状态只能转换一次
const p = new Promise((resolve, reject) => {
resolve('成功'); // 状态变为 fulfilled
reject('失败'); // 无效,状态已确定
});
Promise 链式调用原理
JavaScript
// then 返回新 Promise
Promise.resolve(1)
.then(x => x + 1) // 返回 Promise(2)
.then(x => x * 2) // 返回 Promise(4)
.then(console.log); // 4
// 每个 then 创建新 Promise
// 前一个 Promise 的结果传递给下一个
值穿透
JavaScript
// then 不传回调时,值会穿透
Promise.resolve(1)
.then() // 穿透
.then() // 穿透
.then(console.log); // 1
// 等价于
Promise.resolve(1)
.then(x => Promise.resolve(x))
.then(x => Promise.resolve(x))
.then(console.log);
Promise 解析
JavaScript
// then 回调返回 Promise 时会等待解析
Promise.resolve(1)
.then(x => new Promise(resolve => {
setTimeout(() => resolve(x + 1), 1000);
}))
.then(console.log); // 2(等待 1 秒)
// 返回值是 Promise 时,会等待其完成
Promise 错误冒泡
JavaScript
// 错误会沿链冒泡
Promise.reject(new Error('失败'))
.then(() => console.log('跳过'))
.then(() => console.log('跳过'))
.catch(err => console.log('捕获:', err.message));
// catch 后可继续
Promise.reject(new Error('失败'))
.catch(err => '恢复')
.then(result => console.log(result)); // '恢复'
未处理的拒绝
JavaScript
// 未处理的 Promise 拒绝
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的拒绝:', reason);
});
// 避免未处理的拒绝,始终添加 catch
Promise.all 实现
JavaScript
// Promise.all 并行执行
Promise.all([
fetch('/api/users'),
fetch('/api/posts')
])
.then(([users, posts]) => {
// 全部成功
})
.catch(err => {
// 任一失败立即触发
});
// 手动实现
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let completed = 0;
promises.forEach((p, i) => {
Promise.resolve(p).then(result => {
results[i] = result;
completed++;
if (completed === promises.length) {
resolve(results);
}
}).catch(reject);
});
});
}
Promise.race 实现
JavaScript
// Promise.race 返回最先完成的结果
Promise.race([
fetch('/api/fast'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('超时')), 5000)
)
]);
// 手动实现
function promiseRace(promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
Promise.resolve(p).then(resolve).catch(reject);
});
});
}
async 函数原理
JavaScript
// async 函数返回 Promise
async function foo() {
return '结果';
}
// 等价于
function foo() {
return Promise.resolve('结果');
}
// await 暂停函数执行
async function bar() {
const result = await promise;
// 等待 promise 完成后继续
}
async 执行流程
JavaScript
async function example() {
console.log('A');
await Promise.resolve();
console.log('B');
}
example();
console.log('C');
// 执行顺序:
// A (同步执行到 await)
// C (await 后的代码作为微任务)
// B (微任务执行)
await 并行优化
JavaScript
// ❌ 顺序 await(串行)
async function serial() {
const a = await fetchA(); // 1秒
const b = await fetchB(); // 1秒
return [a, b]; // 总耗时 2秒
}
// ✅ 并行 await
async function parallel() {
const [a, b] = await Promise.all([
fetchA(),
fetchB()
]);
return [a, b]; // 总耗时 1秒
}
// ✅ 先发起再 await
async function optimized() {
const pA = fetchA();
const pB = fetchB();
const a = await pA;
const b = await pB;
return [a, b]; // 总耗时 1秒
}
错误处理策略
JavaScript
// try-catch 捕获 await 错误
async function withTryCatch() {
try {
const data = await fetchData();
return processData(data);
} catch (err) {
console.error('错误:', err);
throw err;
}
}
// .catch() 捕获 Promise 错误
async function withCatch() {
const data = await fetchData().catch(err => {
console.error('错误:', err);
return null; // 恢复默认值
});
return data;
}
// 包装器模式
async function withWrapper(fn) {
try {
return await fn();
} catch (err) {
logger.error(err);
return null;
}
}
Promise 性能考量
JavaScript
// Promise 创建开销
// 避免 Promise 包装同步操作
// ❌ 不必要的 Promise
function syncOperation() {
return Promise.resolve(syncWork());
}
// ✅ 直接返回
function syncOperation() {
return syncWork();
}
// 使用 async_hooks 监控
const asyncHooks = require('async_hooks');
const hook = asyncHooks.createHook({
init(asyncId, type) {
if (type === 'PROMISE') {
console.log('Promise 创建:', asyncId);
}
}
});
要点总结
- Promise 状态只能转换一次,pending → fulfilled/rejected
- then 返回新 Promise,形成链式调用
- 错误沿链冒泡,catch 可捕获恢复
- async 函数返回 Promise,await 暂停执行
- await 后代码作为微任务执行
- 并行操作使用 Promise.all 或先发起再 await
📝 发现内容有误?点击此处直接编辑