Error handling in JavaScript can be easy while being tricky at certain places, especially Promises. JS allows error handling with the help of try, catch, and throw.
const main = () => {
try {
// Do something super crazy
if (!bakePizza()) {
throw new Error('Oh no!');
}
} catch (error) {
// That escalated quickly
handleOvenExplosion();
}
}
This seems simple enough but gets a little tricky when Promises are involved.
Let’s look at a simple example of a Promise. The following Promise function fetches a list of user profiles from the database, where the result set is resolved by the promise function and the error is rejected.
const userProfileQuery = new Promise((resolve, reject) => {
connection.query('SELECT * FROM Users', [], (err, result) => {
if (err) reject({ type: 'SQL', err});
connection.release();
resolve(result);
});
userProfileQuery
.then((data) => {
const userList = data;
// Do something crazy with the list
})
.catch((err) => {
// Oh, snap!
// Handle error
});
In an ideal world, we’d want to have a single try-catch block to handle all errors that occur in that single file.
const { getUserProfiles } = require('./helpers');
module.exports = () => {
try {
let userProfileList;
getUserProfiles
.then((data) => {
userProfileList = data;
})
.catch((error) => {
// Handle Promise Error
// All errors thrown in this promise land here
});
} catch (error) {
// Handle errors in this module
}
}
The above module is simple — It fetches a list of user profiles with the help of a Promise function.
But the problem with the above module is that when we throw
a new Error
inside the then
block of the promise, it will always pass to the catch
block of the promise. That is because throwing a new error inside a then
block of a promise will always be passed to the catch
block of the invoking promise function. This does not allow us to handle all errors in a module with a singular try-catch block.
But, alas! There is a way to handle this with the help of Async/Await. Let me explain this better with an example:
const { getUserProfiles } = require('./helpers');
module.exports = async () => {
try {
const userProfileList = await getUserProfiles;
} catch (error) {
// Handle errors in this module
switch (type) {
case ERROR_SQL:
// Handle SQL errors
default:
// Handle common errors
}
}
}
This little addition of async/await in your code does two things
Assign the value to the variable which was resolved by the promise function.
Throw error if the promise function rejects anything.
Note that the value assignment works only when a promise functions resolves some value and errors get thrown only when the promise function rejects something.
This way, async/await lets us keep our code clean, maintainable, and easy to read.
Thanks for reading. If you have thoughts on this, be sure to leave a comment.
#javascript #nodejs #node