Node.js for PHP Programmers #3: Exceptions and Errors

Just like PHP, JavaScript supports Exceptions - only they're called Errors. However, due to the asynchronous nature of Node.js, the classic try/catch strategy doesn't work. To catch asynchronous errors, Node strongly encourages a particular signature for callback functions. I'll explain all that shortly, but now I have a confession to make.Node.js for PHP Programmers #3: Exceptions and Errors

Sometimes Things Go Wrong

I don't eat meat. It's not because I like animals too much - I don't believe chickens have a soul. It's just that I ate a lot of meat previously in my life, and if every person on this planet eats as much meat as I did, the place will become awful to live in. If you don't believe me, go watch this video on YouTube and thank Prof. Albert Bartlett for the depression. So I quit eating flesh a year ago, and so far my teeth haven't fallen or something.

But to be honest, I still eat meat every once in a while. You see, vegetarians have to take pills to compensate for the nutriments they don't get from the meat. I prefer to eat meat than pills. And since the Sunday meal is often a family moment, and since meat MUST be on the menu, I eat meat almost every Sunday.

And I like meat. That means that I love the Sunday meals, whether it's veal sauté with white wine, or roasted chicken with French fries. And last time I ate too much of it. Then things started going wrong.

The PHP Digestive System

I won't expose in details the miserable Sunday afternoon I spent after that. However, since the audience of this blog is mostly composed of programmers, I can write what happened in PHP.

The Node.js Digestive System

JavaScript is another good way to express my misery. And since Node.js puts a dose of asynchronicity on top of JavaScript, it's a good opportunity to show that digestion can take time. Here is the equivalent of the previous class, written for Node.js:

In digest(), setTimeout() is there to simulate an asynchronous processing. Asynchronous functions don't use the return statement to return their result. Instead, they call the given callback with the result as parameter. That's why digest() ends with callback(nutriments) instead of return $nutriments as in PHP. Therefore, eat() has to provide a callback when calling digest(). When all the elements are processed, eat() can return true by calling its own callback.

Tip: If you don't understand why the line var body = this is necessary, you've not yet encountered the First Gotcha of JavaScript. Head to my article on "this" for more details.

Second Tip: The test if (processedElements == meal.length) is a way to determine when the asynchronous meal digestion is complete. The need to resynchronize asynchronous processing arises quite often in Node.js, and instead of using boilerplate code similar to this one, you should definitely use the async module. It offers resynchronization, and many other things. In my opinion, it should be part of the Node.js core.

Wait, It Doesn't Work

The digest() function is asynchronous, remember? So when Node executes this piece of code from eat():

Node iterates over every element in the meal, executes HumanBody.digest() on all of them, then ends. That means that if a problem of digestive capacity occurs while in digest(), it will be after the end of the eat() execution, and that means after the try {} catch {} statement is finished. The Error will never get caught, and this is dramatic. Imagine a human body unable to vomit even when filled up over the limit? Explosion!

No try/catch in Node.js

In asynchronous programming, the communication channel between the caller function and the callee is the callback. The only solution to overcome the uncaught error is to pass the Error to the callback. So the eat() and digest() functions must be refactored as follows:

The important thing is is the new signature of the callback passed to digest():

The callback expects that an error may be passed by the asynchronous digestion. And it's the responsibility of the callback to deal with this error if it exists, and to proceed with normal operations otherwise.

And it's the only way to catch an error thrown in an asynchronous function. So forget about try {} catch {} in Node.js. You will use them very seldom, while callbacks expecting errors are everywhere.

Always Carry Your Errors With You

What happens with the asynchronous digestion can happen in every asynchronous function in Node.js. Thats's why the core asynchronous functions always show err as the first argument of every callback. If the operation was completed successfully, then the first argument will be null or undefined. Here is an example with the equivalent of PHP's realpath():

When you design your own asynchronous functions, follow this rule of thumb and always put the error as first argument of the callback. It's also a good way to remember that you have to deal with the error cases first.

Conclusion

There is still a flaw in the Node.js HumanBody implementation. If something bad happens in the middle of the meal digestion, the vomiting is triggered and the main callback is called with false as argument. But the forEach() loop continues. That means that the callback may be called a second time with true as argument. The correction is left as an exercise for the reader.

As for my digestive problems, they're over. But "Once bitten, twice shy", so I always think that something bad may happen to me when eating meat, and I'm very careful.

If you don't want to give up meat, you should be careful, too. Always expect that something bad may happen, and you will design robust and effective programs - even in asynchronous Node.js.

Also in this series: Node.js for PHP Programmers #2: Modules, Packages, and the Strawberry House and Node.js for PHP Programmers #4: Streams.

Published on 15 Apr 2012 with tags development JavaScript NodeJS php

comments powered by Disqus