各位老铁,大家好!今天咱们聊聊 JavaScript 里 async/await
这哥俩的异常处理,保证让各位听完之后,再也不用担心代码动不动就崩给你看了。
开场白:别让 async/await
变成你的噩梦
async/await
简化了异步代码的编写,让代码看起来更像同步代码,提高了可读性。但是,如果不对异常进行妥善处理,它也会变成你的噩梦,让你的程序时不时给你来个惊喜的崩溃。
try-catch
:最直接的异常捕获方式
try-catch
块是处理异常的最基本也是最常用的方法。它允许你将可能抛出异常的代码包裹在一个 try
块中,并在 catch
块中处理这些异常。
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('出错了:', error);
// 在这里进行错误处理,比如显示错误信息,重试请求等
return null; // 或者抛出新的错误,取决于你的需求
}
}
async function processData() {
const data = await fetchData();
if(data){
console.log("数据处理结果:", data);
} else {
console.log("数据获取失败,无法处理。");
}
}
processData();
在这个例子中,fetchData
函数使用 try-catch
块来捕获 fetch
请求和 response.json()
解析过程中可能发生的任何错误。如果在 try
块中的任何地方发生错误,控制权将立即转移到 catch
块,在那里你可以处理错误并采取适当的措施。
Promise.prototype.catch()
:另一种选择
Promise.prototype.catch()
方法是另一种处理 async/await
函数中异常的方式。它允许你为 Promise
对象添加一个错误处理程序。
async function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.catch(error => {
console.error('出错了:', error);
// 在这里进行错误处理
return null; // 或者抛出新的错误
});
}
async function processData() {
const data = await fetchData();
if(data){
console.log("数据处理结果:", data);
} else {
console.log("数据获取失败,无法处理。");
}
}
processData();
在这个例子中,fetchData
函数使用 Promise.prototype.catch()
方法来捕获 fetch
请求和 response.json()
解析过程中可能发生的任何错误。 注意 catch
是链式调用.then()
之后添加的。
try-catch
vs. Promise.prototype.catch()
:该选哪个?
这两种方法都可以用来处理 async/await
函数中的异常,但它们在一些方面有所不同。
特性 | try-catch |
Promise.prototype.catch() |
---|---|---|
适用范围 | 适用于同步和异步代码 | 仅适用于 Promise 对象 |
语法 | 更加结构化,易于阅读和理解 | 更加分散,可能需要链式调用 |
错误处理位置 | 可以在 try 块中立即处理错误 |
错误处理程序在 Promise 对象解决后才执行 |
适用场景 | 需要在特定代码块中处理错误时 | 需要为整个 Promise 链添加一个全局错误处理程序时 |
可读性 | 通常比 Promise.prototype.catch() 更容易理解和维护 |
在简单的 Promise 链中可能更简洁 |
嵌套的 async/await | 处理嵌套的 async/await 错误更方便 | 需要在每个 Promise 链中添加 catch,否则错误可能会被忽略 |
最佳实践:结合使用,各取所长
一般来说,推荐结合使用 try-catch
和 Promise.prototype.catch()
来处理 async/await
函数中的异常。
- 使用
try-catch
块来捕获函数内部的错误。 这可以让你在特定的代码块中处理错误,并采取适当的措施。 - 使用
Promise.prototype.catch()
方法来为整个Promise
链添加一个全局错误处理程序。 这可以确保即使在try-catch
块中没有捕获到错误,也能得到处理。
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('fetchData 出错了:', error);
throw error; // 重新抛出错误,让外层函数处理
}
}
async function processData() {
try {
const data = await fetchData();
console.log("数据处理结果:", data);
} catch (error) {
console.error('processData 出错了:', error);
// 全局错误处理,比如显示错误页面,记录错误日志等
}
}
processData(); // 这里就不需要 catch 了,因为 processData 内部已经 catch 了
在这个例子中,fetchData
函数使用 try-catch
块来捕获错误,并将错误重新抛出。processData
函数使用 try-catch
块来捕获 fetchData
函数抛出的错误,并进行全局错误处理。
更复杂的例子:多个 await
调用
当 async
函数中包含多个 await
调用时,错误处理变得更加重要。你需要确保每个 await
调用都得到妥善的保护。
async function processMultipleRequests() {
try {
const result1 = await apiRequest1();
const result2 = await apiRequest2(result1); //依赖 result1
const result3 = await apiRequest3(result2); //依赖 result2
console.log('所有请求都成功完成:', result3);
return result3;
} catch (error) {
console.error('发生错误:', error);
// 在这里进行错误处理,例如回滚事务,发送警报等
}
}
async function apiRequest1() {
try {
const response = await fetch('https://api.example.com/api1');
if (!response.ok) {
throw new Error(`请求 1 失败:${response.status}`);
}
return await response.json();
} catch (error) {
console.error('apiRequest1 失败:', error);
throw error; // 重新抛出错误
}
}
async function apiRequest2(data1) {
try {
const response = await fetch('https://api.example.com/api2', {
method: 'POST',
body: JSON.stringify(data1)
});
if (!response.ok) {
throw new Error(`请求 2 失败:${response.status}`);
}
return await response.json();
} catch (error) {
console.error('apiRequest2 失败:', error);
throw error; // 重新抛出错误
}
}
async function apiRequest3(data2) {
try {
const response = await fetch('https://api.example.com/api3', {
method: 'PUT',
body: JSON.stringify(data2)
});
if (!response.ok) {
throw new Error(`请求 3 失败:${response.status}`);
}
return await response.json();
} catch (error) {
console.error('apiRequest3 失败:', error);
throw error; // 重新抛出错误
}
}
processMultipleRequests();
在这个例子中,processMultipleRequests
函数包含多个 await
调用。每个 await
调用都包裹在一个 try-catch
块中,以捕获可能发生的错误。如果任何一个 await
调用失败,错误将被捕获,并进行相应的处理。 每个 apiRequest
函数都将错误向上抛出,最终由 processMultipleRequests
的 catch
块处理。
错误处理策略:具体情况具体分析
错误处理策略应该根据你的具体需求来制定。以下是一些常见的错误处理策略:
-
重试请求: 如果请求失败,可以尝试重新发送请求。这对于网络连接不稳定等临时性错误非常有用。
async 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(`Attempt ${i + 1} failed:`, error); if (i === maxRetries - 1) { throw error; // 所有重试都失败,抛出错误 } await new Promise(resolve => setTimeout(resolve, 1000)); // 等待 1 秒后重试 } } }
-
显示错误信息: 如果请求失败,可以向用户显示错误信息。这可以帮助用户了解发生了什么问题,并采取相应的措施。
async function fetchDataAndDisplayError() { try { const data = await fetchData('https://api.example.com/data'); console.log('数据:', data); } catch (error) { alert('获取数据失败:' + error.message); } }
-
回滚事务: 如果在事务中发生错误,可以回滚事务,以确保数据的一致性。
async function processTransaction() { try { await startTransaction(); await updateDatabase(); await commitTransaction(); console.log('事务成功完成'); } catch (error) { console.error('事务失败:', error); await rollbackTransaction(); console.log('事务已回滚'); } }
-
发送警报: 如果发生严重的错误,可以发送警报,通知管理员。
async function processDataAndAlert() { try { const data = await fetchData(); // ... 处理数据 } catch (error) { console.error('处理数据失败:', error); await sendAlert('数据处理失败:' + error.message); } }
-
记录错误日志: 记录错误日志可以帮助你诊断和解决问题。
async function fetchDataAndLogErrors() { try { const data = await fetchData(); // ... 处理数据 } catch (error) { console.error('获取数据失败:', error); await logError('获取数据失败:' + error.message); } }
一些额外的建议
- 不要吞噬错误。 除非你确定能够完全处理错误,否则不要吞噬错误。吞噬错误会使你难以诊断和解决问题。 重新抛出错误,或者返回一个包含错误信息的值。
- 使用有意义的错误信息。 错误信息应该能够帮助你了解发生了什么问题,以及如何解决问题。
- 测试你的错误处理代码。 确保你的错误处理代码能够正常工作,并能够处理各种不同的错误情况。
- 考虑使用错误跟踪工具。 错误跟踪工具可以帮助你监控和分析错误,并更快地找到和解决问题。例如 Sentry, Bugsnag 等。
- 保持代码简洁和可读性。 复杂的错误处理逻辑可能会使代码难以阅读和理解。尽量保持代码简洁和可读性,并使用注释来解释复杂的逻辑。
总结:拥抱异常,掌控代码
async/await
的异常处理是编写健壮的 JavaScript 代码的关键。通过结合使用 try-catch
块和 Promise.prototype.catch()
方法,你可以确保你的代码能够优雅地处理各种错误情况,并避免程序崩溃。 记住,拥抱异常,掌控代码! 祝各位老铁编码愉快!
希望今天的讲座对大家有所帮助。如果有什么问题,欢迎随时提问。