How To Handle Errors in Asynchronous Javascript Code (when working with Callbacks)

Sherlynn Tan
Aug 18 · 3 min read

We will use a simple function called calculateSquare to demonstrate how to handle errors in asynchronous Javascript code.

In order to simulate an asynchronous function, we will use setTimeout:

function calculateSquare(number, callback) {  
setTimeout(() => {
const result = number * number;
callback(result);
}, 1000);
}
let callback = (result) => {
console.log('Result from calculateSquare: ' + result)
}
calculateSquare(2, callback);

Run the code above and everything works.

But what happens when this function gets a String instead of a Number type? We would get NaN (not a number) for the result. This needs to be handled.

function calculateSquare(number, callback) {
setTimeout(() => {
if (typeof number !== "number") {
throw new Error("Argument of type number is expected.");
return;
}
const result = number * number;
callback(null, result);
}, 1000);
}
let callback = (result) => {
console.log('Result from calculateSquare: ' + result)
}
Uncaught Error.

If we run the above code, we will get an Uncaught Error instead of a Caught error from our catch block.

That’s because by the time the Caught error message (from the catch block) has been called, the surrounding code has already been executed before it.

setTimeout will place our callback function in Javascript’s message queue while the try-catch block and other surrounding code resides in the call stack. Everything from the call stack will be executed before we even get to the first item of the message queue. (Read more about the event loop here).

Photo from: dev.to

Traditionally for such cases, errors are handled as arguments of the callback.
The first argument of the callback is usually named error, whereby if something goes wrong in the asynchronous function, then the callback gets called with the first argument which specifies what error has happened.
If everything goes well, then the first argument will be null.
This is called error-first callback, which is a common pattern used in Javascript, especially in NodeJS.

Modified as such:

function calculateSquare(number, callback) {
setTimeout(() => {
if (typeof number !== "number") {
callback(new Error("Argument of type number is expected."));
return;
}
const result = number * number;
callback(null, result);
}, 1000);
}
let callback = (error, result) => {
if (error !== null) {
console.log("Caught error: " + String(error));
return;
}
console.log(result);
};

Now our error goes into the “Caught error” defined by us, rather than being thrown the “Uncaught error”.

Hope this clarifies and helps someone! Cheers.

Credits and code example: Viktor Pyskunov

Sherlynn Tan

Written by

Software Developer at Thoughtworks | Marathon Runner | Ex-Pharmacist Writing on Coding, Technology and Running

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade