An Interest In:
Web News this Week
- March 21, 2024
- March 20, 2024
- March 19, 2024
- March 18, 2024
- March 17, 2024
- March 16, 2024
- March 15, 2024
Better error handling with async/await
This article is intended to suggest a better way to handle errors when using async/await syntax. Prior knowledge of how promises work is important.
From Callback Hell to Promises
Callback hell makes your code drift to the right instead of downward due to multiple nesting of callback functions.
I wont go into details of what callback hell is, but I'll give an example of how it looks.
User profile example 1
// Code that reads from left to right // instead of top to bottomlet user;let friendsOfUser;getUser(userId, function(data) { user = data; getFriendsOfUser(userId, function(friends) { friendsOfUser = friends; getUsersPosts(userId, function(posts) { showUserProfilePage(user, friendsOfUser, posts, function() { // Do something here }); }); });});
Promises
Promises were introduced to the Javascript(ES6) language to handle asynchronous operations better without it turning into a callback hell.
The example below use promises to solve callback hell by using multiple chained .then
calls instead of nesting callbacks.
User profile example 2
// A solution with promiseslet user;let friendsOfUser;getUser().then(data => { user = data; return getFriendsOfUser(userId);}).then(friends => { friendsOfUser = friends; return getUsersPosts(userId);}).then(posts => { showUserProfilePage(user, friendsOfUser, posts);}).catch(e => console.log(e));
The solution with promise looks cleaner and more readable.
Promises with with async/await
Async/await is a special syntax to work with promises in a more concise way.
Adding async before any function
turns the function into a promise.
All
async
functions return promises.
Example
// Arithmetic addition functionasync function add(a, b) { return a + b;}// Usage: add(1, 3).then(result => console.log(result));// Prints: 4
Making the User profile example 2
look even better using async/await
User profile example 3
async function userProfile() { let user = await getUser(); let friendsOfUser = await getFriendsOfUser(userId); let posts = await getUsersPosts(userId); showUserProfilePage(user, friendsOfUser, posts);}
Wait! there's a problem
If theres a promise rejection in any of the request in User profile example 3
, Unhandled promise rejection
exception will be thrown.
Before now Promise rejections didn't throw errors. Promises with unhandled rejections used to fail silently, which could make debugging a nightmare.
Thank goodness promises now throws when rejected.
Google Chrome throws:
VM664:1 Uncaught (in promise) Error
Node will throw something like:
(node:4796) UnhandledPromiseRejectionWarning: Unhandled promise rejection (r ejection id: 1): Error: spawn cmd ENOENT
[1] (node:4796) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
No promise should be left uncaught. - Javascript
Notice the .catch
method in User profile example 2
.
Without the .catch block Javascript will throw Unhandled promise rejection
error when a promise is rejected.
Solving this issue in User profile example 3
is easy. Unhandled promise rejection
error can be prevented by wrapping await operations in a try...catch block:
User profile example 4
async function userProfile() { try { let user = await getUser(); let friendsOfUser = await getFriendsOfUser(userId); let posts = await getUsersPosts(userId); } catch(e) { console.log(e); } showUserProfilePage(user, friendsOfUser, posts);}
Problem solved!
...But error handling could be improved
How do you know with error is from which async request?
We can call a .catch
method on the async requests to handle errors.
User profile example 5
let user = await getUser().catch(e => console.log('Error: ', e.message));let friendsOfUser = await getFriendsOfUser(userId).catch(e => console.log('Error: ', e.message));let posts = await getUsersPosts(userId).catch(e => console.log('Error: ', e.message));showUserProfilePage(user, friendsOfUser, posts);
The solution above will handle individual errors from the requests, but its a mix of patterns. There should be a cleaner way to use async/await without using .catch
method (Well, you could if you don't mind).
Here's my solution to a better async/await error handling
User profile example 6
/** * @description ### Returns Go / Lua like responses(data, err) * when used with await * * - Example response [ data, undefined ] * - Example response [ undefined, Error ] * * * When used with Promise.all([req1, req2, req3]) * - Example response [ [data1, data2, data3], undefined ] * - Example response [ undefined, Error ] * * * When used with Promise.race([req1, req2, req3]) * - Example response [ data, undefined ] * - Example response [ undefined, Error ] * * @param {Promise} promise * @returns {Promise} [ data, undefined ] * @returns {Promise} [ undefined, Error ] */const handle = (promise) => { return promise .then(data => ([data, undefined])) .catch(error => Promise.resolve([undefined, error]));}async function userProfile() { let [user, userErr] = await handle(getUser()); if(userErr) throw Error('Could not fetch user details'); let [friendsOfUser, friendErr] = await handle( getFriendsOfUser(userId) ); if(friendErr) throw Error('Could not fetch user\'s friends'); let [posts, postErr] = await handle(getUsersPosts(userId)); if(friendErr) throw Error('Could not fetch user\'s posts'); showUserProfilePage(user, friendsOfUser, posts);}
Using the handle
utility function, we are able to avoid Unhandled promise rejection
error and also handle error granularly.
Explanation
The handle
utility function takes a promise as an argument and always resolves it, returning an array with [data|undefined, Error|undefined]
.
- If the promise passed to the
handle
function resolves it returns[data, undefined]
; - If it was rejected, the
handle
function still resolves it and returns[undefined, Error]
Similar solutions
Conclusion
Async/await has a clean syntax, but you still have to handle thrown exceptions in async functions.
Handling error with .catch
in promise .then
chain can be difficult unless you implement custom error classes.
Using the handle
utility function, we are able to avoid Unhandled promise rejection
error and also handle error granularly.
Original Link: https://dev.to/sobiodarlington/better-error-handling-with-async-await-2e5m
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To