JavaScript中的异步错误处理与Promise链的错误传播
字数 472 2025-11-26 11:34:09
JavaScript中的异步错误处理与Promise链的错误传播
在JavaScript异步编程中,错误处理是一个关键课题。与同步代码使用try-catch不同,异步操作中的错误需要通过特定机制进行捕获和处理。
1. 异步错误处理的基本挑战
// 同步错误可以正常捕获
try {
throw new Error('同步错误');
} catch (error) {
console.log('捕获到:', error.message); // 正常捕获
}
// 异步错误无法用try-catch捕获
try {
setTimeout(() => {
throw new Error('异步错误');
}, 0);
} catch (error) {
console.log('这行不会执行'); // 无法捕获异步错误
}
原因:异步操作会被放入任务队列,当错误发生时,同步的try-catch块已经执行完毕。
2. Promise的错误处理机制
Promise提供了专门的错误处理方式:
// 方式1:使用catch方法
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
console.log('捕获Promise链中的错误:', error);
});
// 方式2:then的第二个参数(不推荐,有局限性)
fetch('/api/data')
.then(
response => response.json(),
error => console.log('只能捕获当前then的错误:', error)
);
3. Promise链的错误传播特性
Promise链具有"冒泡"特性,错误会一直向后传递,直到被catch捕获:
function asyncOperation() {
return new Promise((resolve, reject) => {
Math.random() > 0.5 ? resolve('成功') : reject(new Error('操作失败'));
});
}
asyncOperation()
.then(result => {
console.log('第一步:', result);
return asyncOperation(); // 可能再次失败
})
.then(result => {
console.log('第二步:', result);
return '所有操作完成';
})
.catch(error => {
console.log('捕获整个链路的错误:', error.message);
return '降级结果';
})
.then(finalResult => {
console.log('最终结果:', finalResult);
});
4. 错误处理的实践技巧
// 技巧1:在catch中返回正常值,让链条继续
fetch('/api/main')
.catch(error => {
console.log('主API失败,使用备用数据');
return { data: '备用数据' };
})
.then(data => processData(data));
// 技巧2:重新抛出错误
fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
})
.catch(error => {
if (error.message.includes('404')) {
// 处理特定错误
return { data: [] };
}
// 其他错误继续抛出
throw error;
})
.catch(error => {
console.log('最终错误处理:', error);
});
5. async/await中的错误处理
async函数提供了更直观的错误处理方式:
// 方式1:try-catch(推荐)
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.log('捕获async函数错误:', error);
throw error; // 可以继续抛出
}
}
// 方式2:在调用处使用catch
fetchData()
.then(data => console.log(data))
.catch(error => console.log('外部捕获:', error));
// 方式3:Promise.all的错误处理
async function fetchMultiple() {
try {
const [user, posts] = await Promise.all([
fetch('/api/user'),
fetch('/api/posts')
]);
return { user, posts };
} catch (error) {
// 任何一个Promise被拒绝都会进入catch
console.log('多个请求中有错误:', error);
}
}
6. 未处理错误的全局捕获
// 浏览器环境
window.addEventListener('unhandledrejection', event => {
console.log('未处理的Promise拒绝:', event.reason);
event.preventDefault(); // 阻止默认错误输出
});
// Node.js环境
process.on('unhandledRejection', (reason, promise) => {
console.log('未处理的Promise拒绝:', reason);
});
关键要点总结:
- Promise错误具有冒泡特性,会向后传递直到被捕获
- catch方法应该放在链条末尾来捕获所有错误
- async/await可以使用try-catch,但返回的仍是Promise
- 重要的错误应该重新抛出,让调用方知晓
- 始终设置全局未处理错误监听,避免静默失败