Promise 的错误处理是使用 Promise 时必须掌握的重要技能。正确的错误处理可以确保程序的健壮性,避免未捕获的错误导致程序崩溃。
错误处理的基本方法
1. 使用 .catch() 方法
.catch() 是 Promise 错误处理的主要方法,它会捕获链中任何地方抛出的错误:
javascriptPromise.resolve() .then(() => { throw new Error('出错了'); }) .catch(error => { console.error('捕获到错误:', error.message); });
2. 在 .then() 的第二个参数中处理
.then() 方法可以接收两个参数:成功回调和失败回调:
javascriptPromise.resolve() .then( result => console.log('成功:', result), error => console.error('失败:', error.message) );
注意:这种方式的错误处理只捕获前一个 Promise 的错误,不会捕获链中后续的错误。
错误传播机制
错误会沿着 Promise 链向下传播
javascriptPromise.resolve() .then(() => { throw new Error('第一个错误'); }) .then(() => { console.log('这行不会执行'); }) .then(() => { console.log('这行也不会执行'); }) .catch(error => { console.error('最终捕获:', error.message); // 输出: 最终捕获: 第一个错误 });
错误可以被捕获后恢复
javascriptPromise.resolve() .then(() => { throw new Error('出错了'); }) .catch(error => { console.error('捕获错误:', error.message); return '恢复后的值'; // 返回一个值,链可以继续 }) .then(value => { console.log('继续执行:', value); // 输出: 继续执行: 恢复后的值 });
常见错误处理场景
1. 网络请求错误处理
javascriptfetch('/api/data') .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { console.log('数据:', data); }) .catch(error => { console.error('请求失败:', error.message); // 可以在这里显示错误提示给用户 showErrorToUser('数据加载失败,请稍后重试'); });
2. 多个 Promise 的错误处理
javascriptPromise.all([promise1, promise2, promise3]) .then(results => { console.log('全部成功:', results); }) .catch(error => { console.error('至少一个失败:', error.message); });
3. 使用 Promise.allSettled 处理部分失败
javascriptPromise.allSettled([promise1, promise2, promise3]) .then(results => { results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`Promise ${index} 成功:`, result.value); } else { console.error(`Promise ${index} 失败:`, result.reason); } }); });
错误处理的最佳实践
1. 总是添加错误处理
javascript// 不推荐:没有错误处理 fetch('/api/data') .then(response => response.json()) .then(data => console.log(data)); // 推荐:添加错误处理 fetch('/api/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error));
2. 使用 finally 进行清理
javascriptlet isLoading = true; fetch('/api/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error)) .finally(() => { isLoading = false; console.log('请求完成,无论成功或失败'); });
3. 错误处理要具体
javascript// 不推荐:笼统的错误处理 Promise.resolve() .catch(error => { console.error('出错了'); }); // 推荐:具体的错误处理 Promise.resolve() .catch(error => { if (error instanceof NetworkError) { console.error('网络错误:', error.message); } else if (error instanceof ValidationError) { console.error('验证错误:', error.message); } else { console.error('未知错误:', error.message); } });
4. 考虑错误恢复策略
javascriptasync function fetchDataWithRetry(url, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error(`尝试 ${i + 1} 失败:`, error.message); if (i === maxRetries - 1) { throw error; // 最后一次尝试失败,抛出错误 } // 等待一段时间后重试 await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); } } }
async/await 中的错误处理
使用 try/catch
javascriptasync function fetchData() { try { const response = await fetch('/api/data'); const data = await response.json(); console.log('数据:', data); } catch (error) { console.error('错误:', error.message); // 错误处理逻辑 } }
捕获特定错误
javascriptasync function fetchData() { try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { if (error.name === 'TypeError') { console.error('网络连接问题'); } else if (error.message.includes('HTTP error')) { console.error('服务器错误'); } else { console.error('未知错误:', error); } throw error; // 可以选择重新抛出错误 } }
未捕获的 Promise 错误
全局错误处理
javascript// 处理未捕获的 Promise 错误 window.addEventListener('unhandledrejection', event => { console.error('未捕获的 Promise 错误:', event.reason); // 可以在这里记录错误或显示错误提示 event.preventDefault(); // 阻止默认的错误输出 }); // Node.js 环境中 process.on('unhandledRejection', (reason, promise) => { console.error('未捕获的 Promise 错误:', reason); });
常见错误处理陷阱
1. 忘记在链中添加 catch
javascript// 危险:没有错误处理 Promise.reject('出错了') .then(result => console.log(result)); // 错误会冒泡到全局,可能导致程序崩溃 // 安全:添加错误处理 Promise.reject('出错了') .then(result => console.log(result)) .catch(error => console.error(error));
2. 在 catch 中忘记重新抛出错误
javascript// 可能导致问题:错误被吞掉 Promise.reject('出错了') .catch(error => { console.error('捕获错误:', error); // 忘记重新抛出,后续代码会继续执行 }) .then(() => { console.log('这行会执行,即使前面有错误'); }); // 推荐:根据情况决定是否重新抛出 Promise.reject('出错了') .catch(error => { console.error('捕获错误:', error); // 如果错误无法恢复,重新抛出 throw error; });
3. 混用 then 的第二个参数和 catch
javascript// 不推荐:容易混淆 Promise.resolve() .then( result => console.log('成功'), error => console.error('失败1') ) .catch(error => console.error('失败2')); // 推荐:统一使用 catch Promise.resolve() .then(result => console.log('成功')) .catch(error => console.error('失败'));
总结
- 总是添加错误处理:使用
.catch()或try/catch - 理解错误传播:错误会沿着 Promise 链向下传播
- 使用 finally 清理:无论成功或失败都要执行的代码放在
finally中 - 具体化错误处理:根据错误类型进行不同的处理
- 考虑错误恢复:实现重试机制或降级策略
- 避免错误吞掉:确保错误被正确处理或重新抛出
- 全局错误监控:使用全局错误处理器捕获未处理的错误