Understanding Error Handling in Async/Await Functions
Written on
Introduction to Async Functions
In JavaScript, excelling at asynchronous programming is essential. Async functions and promises are fundamental components of this non-blocking approach. While various methods exist, async is frequently regarded as the ultimate solution for asynchronous tasks. However, here we will concentrate on error management instead of contrasting async functions with promises.
The Try/Catch Method
Example 1: Using Try/Catch
function getUserInfo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('request exception');}, 1000);
});
}
async function loggedIn() {
try {
let userInfo = await getUserInfo();
// Execution interrupt
let pageInfo = await getPageInfo(userInfo?.userId);
} catch (e) {
console.warn(e);}
}
loggedIn();
When this code runs, it will catch the 'request exception' and halt the execution of getUserInfo, which is a sensible approach. This pattern is particularly useful when there are dependencies between API calls.
However, some developers believe that using try/catch can be overly verbose for each API call, so let's examine a different approach.
The Direct Catch Approach
Example 2: Direct Catch
function getUserInfo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('request exception');}, 1000);
});
}
async function loggedIn() {
let userInfo = await getUserInfo().catch(e => console.warn(e));
// Execution continues; userInfo might be undefined
if (!userInfo) return;
let pageInfo = await getPageInfo(userInfo?.userId);
}
loggedIn();
In this scenario, catch handles the error, but the execution continues, which might result in userInfo being undefined. To mitigate this, a non-null check could be added, though some might find this counterintuitive since one might expect the execution to stop upon encountering an error.
Optimization with Promise.reject
Example 3: Reject in Catch
function getUserInfo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('request exception');}, 1000);
});
}
async function loggedIn() {
let userInfo = await getUserInfo().catch(e => {
console.warn(e);
return Promise.reject(e);
});
// Execution interrupt
let pageInfo = await getPageInfo(userInfo?.userId);
}
loggedIn();
This approach allows for the execution to be halted by including Promise.reject(e) within the catch block.
Suggestions
If the await operation does not require interruption upon an error, consider:
let userInfo = await getUserInfo().catch(e => console.warn(e));
if (!userInfo) return;
If interruption is essential and console errors are a concern:
try {
let userInfo = await getUserInfo();
// Execution interrupt
let pageInfo = await getPageInfo(userInfo?.userId);
} catch (e) {
console.warn(e);}
If awaiting interruption is needed, but console errors are not a concern:
let userInfo = await getUserInfo().catch(e => {
console.warn(e);
return Promise.reject(e);
});
// Execution interrupt
let pageInfo = await getPageInfo(userInfo?.userId);
Summarize
There are various approaches to handling errors in async functions. The try/catch method aligns with our instincts and synchronous programming practices, effectively preventing uncaught promise errors in the console.
On the other hand, the advanced catch method resembles the traditional promise callback pattern. Given the more elegant async/await syntax, it is beneficial to utilize it for improved readability and maintainability of the code.
Ultimately, select the method that best suits your project's requirements and offers the clearest logical flow for your fellow developers. Happy coding!