Why asynchronous exceptions are uncatchable

You have a block of code that throws an exception. You have tried putting it inside a try..catch block but it still does not get caught. You have been told the error is thrown asynchronously, but you are not sure what is exactly going on. You would like to understand why your code doesn't catch it.

1 try
 {
2     // perform work asynchronously that
3     // throws error at some point
4 } catch (e) {
5     // catch error.. doesn't work
6 }

What is asynchronous code

Asynchronous function is a function that performs an operation in the background leveraging from services provided by the JavaScript host and the operating system. What is visible in JavaScript to such call is 1) the initial setting up and initialization of the call and 2) the point when the callback is called and the operation is complete.

Throwing an asynchronous exception means that an exception is thrown from somewhere in an asynchronously executed callback.

Throwing exception asynchronously

The following code reproduces the example.

1 try {
2     setImmediate(() => {
3         throw new Error();
4     });
5 } catch (e) {
6     // catch error.. doesn't work
7 }

Here a try.. catch block is used to wrap a call to setImmediate(). It is a function that operates asynchronously and schedules the argument callback to be called in the near future, as soon as other operations have finished. There are no other statements to execute and the control is passed back to one level up — to the Node.js event loop.

While, in this example, you could add another try.. catch block inside the setImmediate() call, in real life you are dealing with 3rd party code where you have no such possibility.

Pseudocode of event loop

In order to better understand what is taking place one level up from the user code, let us write the event loop in JavaScript pseudocode. Pseudocode is something that describes a concept at a higher level possibly omitting implementation details. In real life the event loop in Node.js is written in C.

The event loop is an infinite while loop that is entered as the last thing when Node.js process is started. Its responsibility is to repeatedly take events and fire any listeners registered for them one at a time.

It can be described by combining two API functions in a while loop.

1 var event;
2 while (event = getNextEvent()) {
3     getListeners(event).forEach((listener) => {
4         listener(event);
5     });
6 }

Here getNextEvent() returns the next event. If there are no events available, it will block until new events occur. The getListeners() function returns an array of listeners that were waiting for the given event.

Run in two passes

Let us take the example and highlight the two parts that are executed at separate times.

1 try {
2     setImmediate(() => {
3         throw new Error();
4     });
5 } catch (e) {
6     // catch error.. doesn't work
7 }

Here the purple section is first executed in one pass. After that pass has finished the control is passed one level up. Then after other operations have finished the setImmediate() contract is fulfilled and the green section is executed.

Let us combine this with the event loop pseudocode. We will replace the listener calling in the pseudocode with the actual code that will be executed. The first pass looks like

1 while (event = getNextEvent()) {
2     getListeners(event).forEach((listener) => {
3         try {
4             setImmediate(...);
5         } catch (e) {
6             // catch error.. doesn't work
7         }
8     });
9 }

And the second part, after the "immediately after other operations have finished" event has occurred, looks like

1 while (event = getNextEvent()) {

2     getListeners(event).forEach((listener) => {
3         throw new Error();
4     });
5 }

From the second part it can be seen that the original catch block that was intended to be there is not present. When the code is executed asynchronously, the original synchronous catch block is not present. In this case, the exception will propagate all the way up to the Node.js internals and will cause the program to exit prematurely.

Catch block is not there when asynchronous code is executed

Asynchronous exception is uncatchable because the intended catch block is not present when the asynchronous code is executed. Instead, the exception will propagate all the way and terminate the program.

Related articles

Asynchronous calls - the surest way
to get lost with Node.js
Asynchronous Call Tree
What if you could turn any spec into working code?
GET FREE LESSON
Share article: