Node.js 101: Error Handling

"Why handle errors?"

Because as a programmer, catching errors is part of your job. You need to know when a disk write fails, a database query times out, or a web service returns zero bytes. You need to be able to react to exceptional (and not-so-exceptional) conditions, and do The Right Thing, even if The Right Thing is merely to print out that error and exit.

"I can just throw an exception, right?"

In some languages, this might work. In Node.js, not so much. Remember, each time a callback is executed, it gets its own stack. Take this code, for example:

function f2() {
    setTimeout(function() {
        throw new Error("I AM ERROR.");
    }, 100);
}

function f1() {
    f2();
}

f1();

Execute that, and here's the stack trace you'll get:

timers.js:96
            if (!process.listeners('uncaughtException').length) throw e;
                                                                      ^
Error: I AM ERROR
    at Object._onTimeout (/Users/doug/tmp/node.js/stack.js:6:9)
    at Timer.ontimeout (timers.js:94:19)

Kinda useless, huh? And that's the problem with callbacks, they get a virgin stack.

"Is there a better way to handle errors?"

There sure is. The convention among many modules in node.js is to create callbacks like these:

function(error, data, ...) {
    if (error) {
        // Handle our error here
        return(null);
    }

    // Handle our data here

}

Here's an example which uses this convention for catching errors:

function talk(cb) {
    cb("Eyes of Ganon are everywhere.");
}

function village(cb) {
    talk(function(error) {
        if (error) {
            cb("village(): " + error);
        }
    });
}

function world(cb) {
    village(function(error) {
        if (error) {
            cb("world(): " + error);
            return(null);
        }
    });
}

world(function(error) {
    if (error) {
        console.log("ERROR: " + error);
        process.exit(1);
    }
});

Run that, and here's the output:

ERROR: world(): village(): Eyes of Ganon are everywhere.

That's right, you now have an error which you can effectively catch, and a traceback! A more real-world example of this might be on a web server, where the error is quietly logged (or displayed to the user), while other callbacks handle other URL requests.

"That code seems kinda... repetitive. And those embedded functions look awkward."

Correct on both counts. "boomerang code" and flow control is something I'll address in a future blog post. In the meantime, enjoy easier handling of errors in your node.js code, and don't hesitate to let me know if you have any questions.

3.357145
Average: 3.4 (14 votes)
Your rating: None