Why throw exceptions in async code?

2,292 views
Skip to first unread message

John Wright

unread,
Apr 5, 2010, 2:51:50 AM4/5/10
to nodejs
Wanted to confirm my newbie async programmer understanding about why
so many libs I see are so likely to easily throw exceptions in async
code.

I like to imagine async programming in terms of a restaurant server
and the kitchen staff. The server has tons of tables to serve and
tons of things to do, he leaves off orders with the kitchen staff so
that he/she can go on and serve other customers. The server can't
block right? There are a variety of ways the kitchen staff can alert
the server of problems with the order ranging from:

A) Yelling at the server the next time they see them - a callback,
pass the error.
B) Tell all the other staff to let them know the order is fucked -
emit an error.
C) Leave a note for them or tell the kitchen manager to tell them -
kind of like setting an error property on the kitchen.
D) Open the door to the restaurant and yelling at the server -
throwing an exception.

Of the 4 options, if I am not off my rocker, option D is obviously the
most disruptive and obnoxious. It basically sends a jolt through the
whole system. If this is true, then why do many node libs immediately
throw exceptions on errors instead of cycling through some of the
other options more carefully.

Example, you are writing a database driver. The database returns an
error, and you immediately throw an exception. But what about
considering that the clients of your lib might want to handle the
error, intelligently acting upon it within the context of a
transaction that is executing concurrently. In this case, setting a
lastError property, and then emitting an error seems smarter. If the
client has still not fixed it you can always throw it later.

Even a lib that has no async interface, could be used by a client in
an async way by wiring it up with async code.

Many of our libs should behave a bit more carefully about throwing
exceptions. Am I wrong?

Isaac Schlueter

unread,
Apr 5, 2010, 3:03:03 AM4/5/10
to nod...@googlegroups.com
The pattern in node is that sync methods throw, and async methods pass
the error as the first argument to the callback. If the first
argument to your callback is falsey (usually null or undefined), then
all is well with the world.

// granted, you could just do fs.readFile("foo", cb),
// but the point is to show the pattern in action.
function readFoo (cb) {
fs.readFile("foo", function (er, data) {
if (er) return cb(er);
cb(null, data);
});
}

function readFooSync () {
// this will throw if it fails.
var data = fs.readFileSync("foo");
return data;
}

Consistently following this pattern allows you to do all kinds of very
creative chaining and error handling quite easily. Throwing in async
code is sloppy.

--i

> --
> You received this message because you are subscribed to the Google Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com.
> To unsubscribe from this group, send email to nodejs+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/nodejs?hl=en.
>
>

Tim Caswell

unread,
Apr 5, 2010, 9:26:58 AM4/5/10
to nod...@googlegroups.com
Also, in Javascript with async code, it's not so much a question of form or preference, but the only way that error handling works consistently.

Basically, the reason that sync functions throw exceptions is because functions can only return one value.  It's a real pain to have to check the return value to see if it's an error message or a real return value.  It's ok to throw exceptions if something exceptional happened and you can't give a meaningful response to the function request.

Now, async code is a different beast all together.  Because of the way try..catch works in JavaScript, it's impossible to catch any exceptions thrown inside the body of a callback function.


Since async code can't catch exceptions we should never throw exceptions in our async callbacks.  However, even if you never throw an exception, some other sync function you call may (built in ones especially).  So to be safe you have to wrap all your sync calls in try..catch blocks and then forward the error on.

If you don't try..catch every block of sync calls, and that's completely understandable, it's ugly and very verbose.  Assuming we're writing an http server, then some piece of code in some http request might throw an exception, and it will cause your whole server to crash hard.  So as a last resort, node has an "uncaughtException" event on the process object. <http://nodejs.org/api.html#_the_tt_process_tt_object>  This will keep your program from crashing, but you loose all variables in the closure (like the http request and response objects).  So you won't be able to send a meaningful response to the browser and the http request will just sit idle till it times out.  This beats crashing your server without warning, but it's still not a good solution.

I'm working on a project with tmpvar <http://github.com/creationix/conductor> that abstracts away all this tedious error catching and checking for sync and async functions.  Also I believe kriszyp's promises do this as well if used right <http://github.com/kriszyp/node-promise>.



Here is an ugly, but safe function:

// Same example but without using Conductor.
// Note the level of error handling done automatically by Conductor.
function loadArticle(name, callback) {
  try {
    var filename = path.join("articles", name + ".markdown");
    Git.readFile(filename, function (err, markdown) {
      if (err) {
        callback(err);
        return;
      }
      try {
        var props = markdownPreParse(markdown);
        props.name = name;
        loadAuthor(props.author, function (err, author) {
          if (err) {
            callback(err);
            return;
          }
          try {
            props.author = author;
            callback(undefined, props);
          } catch (err) {
            callback(err);
          }
        });
      } catch (err) {
        callback(err);
      }
    }
  } catch (err) {
    callback(err);
  }
}

John Wright

unread,
Apr 5, 2010, 4:14:33 PM4/5/10
to nodejs
Thanks guys. I guess it's pretty straightforward then. Thanks for
clarifying the rules.

On Apr 5, 7:26 am, Tim Caswell <t...@creationix.com> wrote:
> Also, in Javascript with async code, it's not so much a question of form or preference, but the only way that error handling works consistently.
>
> Basically, the reason that sync functions throw exceptions is because functions can only return one value.  It's a real pain to have to check the return value to see if it's an error message or a real return value.  It's ok to throw exceptions if something exceptional happened and you can't give a meaningful response to the function request.
>
> Now, async code is a different beast all together.  Because of the way try..catch works in JavaScript, it's impossible to catch any exceptions thrown inside the body of a callback function.
>

> See this thread and gist for more info on thathttp://groups.google.com/group/nodejs/browse_thread/thread/78ad347831...http://gist.github.com/292192

> >> For more options, visit this group athttp://groups.google.com/group/nodejs?hl=en.

zan

unread,
May 3, 2011, 3:25:43 AM5/3/11
to nod...@googlegroups.com
Hi,

Is there any improvement to the way exception handling is done in node? Especially for callbacks? I'm reluctant to add the try/catch for all my callbacks. But it seems to be the only way.

Diogo Resende

unread,
May 3, 2011, 5:44:54 AM5/3/11
to nod...@googlegroups.com

Usually, callbacks have an error argument (usually, the first), that
will tell if something bad happened (or null/undefined otherwise).

Pau

unread,
May 3, 2011, 6:07:31 AM5/3/11
to nod...@googlegroups.com
I would love if everybody was following the pattern Issac mentioned.

I've seen lots of modules using callbacks on sync methods just to be able to return the error...

zan

unread,
May 3, 2011, 9:33:55 AM5/3/11
to nod...@googlegroups.com
how about uncaught exceptions? i'm running a long lived process that uncaught exceptions are something that i need to cater for. from what i see, try/catch blocks and process.uncaughtException are the only ways to deal with those.

billywhizz

unread,
May 3, 2011, 10:35:32 AM5/3/11
to nodejs
I've always felt it would be better to get a return value from sync
methods and deal with it that way. If i'm calling something like
fs.writeSync, i know what possible return values i might get (http://
linux.die.net/man/2/write) and would prefer to check them directly
rather than wrapping the call in a try/catch and not really know what
kind of error object i am going to get back.

e.g.

i think this:

var written = fs.writeSync(fd, buff, 0, buff.length, 0);
if(written < 0) {
// handle the different expected errors
}

is better than this:

try {
var written = fs.writeSync(fd, buff, 0, buff.length, 0);
}
catch(ex) {
// now i have no idea what the exception object looks like without
prior knowledge so i don't really know what to do
// what do i do if it's EAGAIN or EINTR and i need to retry the
operation?
}

for me it makes no sense to throw an exception on a synchronous method
OR on an async one for the reasons outlined above...

the big problem here is that there is no standard way of creating the
exception object that gets returned so different libraries might do it
different ways.

maybe it's just my inherent dislike of c++ over c, but interested to
hear what others think anyway...

Nathan Rajlich

unread,
May 3, 2011, 12:53:10 PM5/3/11
to nod...@googlegroups.com
Well it should be documented how an Error object is meant to be used. In node's case, you can check the "ex.code" property to see what kind of error it was. I don't see any problem with this...

billywhizz

unread,
May 3, 2011, 5:49:54 PM5/3/11
to nodejs
The way node.js throws it's exceptions is just a convention and
doesn't have to be followed by third party libraries.

My feeling is throwing exceptions in js should be avoided at all
costs. it's very expensive from what i have seen. also worth
considering this:

http://groups.google.com/group/nodejs-dev/browse_thread/thread/13e45ef4a0c3a39c/13d33ef8ecc782b3?lnk=gst&q=try+catch#13d33ef8ecc782b3

i'm all for sticking with the current convention of returning an error
object in the callback for async methods but would be very much in
favour of removing the throws in sync methods where possible. it's
just as ugly to me to wrap code in a try/catch as it is to check a
return value using if/else.

Isaac Schlueter

unread,
May 3, 2011, 7:46:02 PM5/3/11
to nod...@googlegroups.com
On Tue, May 3, 2011 at 14:49, billywhizz <apjo...@gmail.com> wrote:
> i'm all for sticking with the current convention of returning an error
> object in the callback for async methods but would be very much in
> favour of removing the throws in sync methods where possible. it's
> just as ugly to me to wrap code in a try/catch as it is to check a
> return value using if/else.

Yeah, try/catch pretty ugly. Almost as ugly as doing sync file system IO :)

Scott R

unread,
May 4, 2011, 11:52:08 AM5/4/11
to nodejs
So it seems the general consensus here is that try/catch is a bad idea
and we should stick to returning Error objects from functions or
supplying them to callbacks instead, and just check for Error
instances? I'm all for avoiding throwing errors, just want to keep it
clear for incoming programmers what the node.js community believes to
be "best practices".

On May 3, 7:46 pm, Isaac Schlueter <i...@izs.me> wrote:

Marcello Bastéa-Forte

unread,
May 4, 2011, 1:53:52 PM5/4/11
to nod...@googlegroups.com
I have no qualms with throwing Errors, especially for programmer errors (e.g. java.lang.IllegalArgumentException equivalent) I can detect immediately.

Obviously once you have any asynchronous exceptions, you're forced to pass it to the callback (or EventEmitter).

Marcello

Bruno Jouhier

unread,
May 4, 2011, 3:38:37 PM5/4/11
to nodejs
There are ways to set up try/catch logic around blocks that contain
async calls. But 1) the patterns are tricky and difficult to code "by
hand", and 2) you need a small wrapper that adds a try/catch around
the callbacks (if you are clever you can avoid these wrappers in
intermediate callbacks).

There is even a pattern for try/finally BTW.

To see this in action, go to http://sage.github.com/streamlinejs/examples/streamlineMe.html
and click on the try/catch button.

Bruno

shaun etherton

unread,
May 5, 2011, 3:17:55 AM5/5/11
to nod...@googlegroups.com
Hey

I always find your explanations/blog, code/comments and the demo above
quite helpful.
Thank you for sharing and taking the time.

cheers
--
shaun

Reply all
Reply to author
Forward
0 new messages