10 tips for coding with Node.js #3: how to know when (not) to throw

By: David Mark Clements

This is part three of a ten part series. Here are the ten tips under consideration:

  1. Develop debugging techniques
  2. How to avail and beware of the ecosystem
  3. How to know when (not) to throw
  4. Reproduce core callback signatures
  5. Use streams
  6. Break out blockers
  7. Deprioritize synchronous code optimizations
  8. Use and create small single-purpose modules
  9. Prepare for scale with microservices
  10. Expect to fail, recover quickly

 

throw

In JavaScript, throwing is a destructive action. It’s the sledgehammer of error propagation.

In this post, we’ll examine scenarios where throwing is appropriate, when not to throw, alternatives to throwing, and best practices for exception handling. This article is quite detailed, you might want to grab a cup of heated liquid mixed with some kind of organic material (such as beans or leaves) and settle into a comfortable chair first.

node.js throwing

Types of errors

Let’s group errors into two broad categories: developer errors and operational errors. Developer errors are bugs in the software: a mistyped variable name, incorrect input, syntax errors and so forth.

Developer errors also include error states that occur from mishandling operational errors. Operational errors are generally predictable issues that occur beyond the control of the program. For instance, network errors or invalid user input.

Native throwing

In many cases, the JavaScript engine will throw when it comes across a developer error. These errors should not be caught, the app needs to fail. In development this allows us to immediately weed out bugs and in production developers can be alerted whilst the process is restarted to retain maximum uptime.

When to throw operation errors

Most operational errors are manageable and should be expected.

For example a dropped connection can be logged and retried, or in some cases replaced with cached content. There’s rarely grounds for throwing due to an operational error.

But there is a subclass of operational errors that could merit a throw. This class of errors is to do with environment misconfiguration.

For instance if a connection is dropping because authentication details are incorrect, we could throw to exit the process (after logging the error).

Almost all configuration happens during initialization, so if we’re throwing post-initialization it could be an over-aggressive approach to an error.

Node.js

When to throw developer errors

Since most programmer errors are caught and thrown by the JavaScript engine, there’s only a small set of developer errors we would ever need to throw on: incorrect usage of a public function or incorrect usage of a command line interface.

In the case of command-line apps we want immediate feedback, throwing can be a way to do that:

#!/usr/bin/env node
if (process.argv.slice(2).length < 2) {
  throw Error('Two args required');
}

Here’s an example of throwing from an exported function:

module.exports = function (secret) { 
  if (!secret) {
     throw Error('..tell me your secret.');
  }
}

Even at that, we may want to question whether we’re giving a best effort. In the example, if the secret isn’t needed outside of our function we could generate one instead (and possibly warn about it, depending on the context).

Or otherwise, can we supply reduced functionality without the secret?

Generally, we only want to throw from functions that perform critical actions. If they’re non-essential then returning, emitting or calling back with an Error object is preferred.

Throwing during initialization to exit the process is appropriate, as a tenet of fail fast. This will quickly alert developers or system admins at the time they’re trying to start the process.

In short, think very carefully before throwing – is there something else we can do?

When not to throw

It’s better to delegate the authority of process crashing to the top level of the application this is why we recommend not throwing inside modules unless they’re intended for use during initialization (and even then, consider allowing the application to decide anyway).

Throwing some time after initialization when the process is supposedly stable is less than ideal – particularly if the application is written as a monolith (all code running in one process, rather than separate micro-services).

If we follow the practice of only throwing when we explicitly expect to exit the process, then the amount of throws in our application should be minimal.

As a rule avoid throwing as much as possible after initialization.

In particular throwing errors that could have been handled without compromising stability, or without making the purpose of the process redundant, is a bad idea.

Never throw in a callback unless the callback is made at the top level of an application and the intent is to crash the process. Callbacks in modules should defer to the caller to decide how to handle an error.

Node.js

Alternatives to throwing

So what should we do for errors that shouldn’t turn into exceptions?

Well that depends on whether we’re dealing with a synchronous or asynchronous operation. Asynchronous operations can be packaged in several ways, the main ones being functions with callback parameters, event emitters and promises. We’ll talk about these as we go, but first let’s talk about throwing alternatives in synchronous functions.

Synchronous functions

In many other languages, the normative approach to synchronous error handling would be to throw. This can be done in JavaScript too:

function tokenize(template) {
  if (notParseable(template)) { throw  Error('couldn\'t parse it guvna');  }
  var tokenObject = parse(template);
  return tokenObject;
}

However in Node.js this approach will either crash the process or demand that the function consumer uses try/catch. There are some cases where a throw is merited, but it should always be considered within a holistic context – do we actually want to crash the process or should we let the invoker decide? If we should let the caller decide, then they must try/catch which will affect performance and cause a process crash if the consumer forgets to try/catch. We’ll talk about try/catch more shortly, but for now let’s look at some alternatives to throwing in a synchronous context.

If we don’t throw from a synchronous function, how do we gracefully handle an issue and propagate the error to the invoking function.

Unlike the standard Node.js-style callback approach there is no agreed-upon normative contract for returning an error state.

One approach sometimes used in the wild is to return null on error. This makes for terser error checking but is both counter intuitive and bug prone.

function tokenize(template) {
  if (notParseable(template)) {  return null; }
  var tokenObject = parse(template);
  return tokenObject;
}

var tokens = tokenize('invalid }}template');
if (!tokens) {
  //handle error
} 

Returning null on error is discouraged, because both error and value states occupy the same space – so the error may accidentally be treated like a value. If the null response isn’t handled and there is an attempt to access a property on the null value, the process will throw.

This defeats the whole point and gives a less useful error.

Beyond this, if type coercion is being applied to an un-handled null return value elsewhere in the application we may get unexpected booleans, or worse a NaN.

Anyone who’s tried to track down the origin of a NaN will testify – this is bad juju.

A fairly standard approach is to return an Error object:

function tokenize(template) {
  if (notParseable(template)) {
    return Error('couldn\'t parse it guvna');
  }
  var tokenObject = parse(template);
  return tokenObject;
}

var tokens = tokenize('invalid }}template');
if (tokens instanceof Error) {
  //handle error
}

It’s ugly, but arguable no uglier than try/catch, and avoids the same disruption and issues associated with throw. However, again both the error state and value state have the same reference, which means the error object could be treated as a correct return value which will invariably create a bug or cause a throw somewhere else in the application. This sort of bug could be difficult to trace to it’s origin point, particularly given the asynchronous nature of JavaScript programs.

A second viable (and cleaner) approach would be to return an object with error and value properties:

function tokenize(template) {
  if (notParseable(template)) {
    return {
      error: Error('couldn\'t parse it guvna')
    };
  }
  var tokenObject = parse(template);
  return {value: tokenObject};
}

var tokens = tokenize('invalid }}template');
if (tokens.error) {
  //handle error
}

In this case, if we forget to handle the error, we’ll at some point try to interact with the value, and which point the app will likely throw. The problem should be easier to identify because we’ll see an object with an error property instead of an object with a value property, and then be able to trace that object to the function that returned it.

A variation of this approach would be to return an array instead of an object, where the first index of the array holds a potential error or null, and the second holds the value. This synchronous imitation of the error-first callback renders a neat compatibility between asynchronous and synchronous API’s (i.e. apply the error-value array to a callback). However the lack of semantic expression (e.g. if(!tokens[0]){} doesn’t communicate meaning well) tends to outweigh the little bit of sugar this gives with calling callbacks.

Yet another technique could be to write synchronous functions in asynchronous style using the standard error first callback contract (see the next section).

Benefits of writing all functions as asynchronous include having a well known standard approach to error propagation and zero refactoring if later down the line our function is changed to make an asynchronous call. Downsides could include developer confusion and additional boilerplate.

A final technique could be to use promises for both synchronous (check out  Promise.resolve) and asynchronous values. Use of promises will affect performance (particularly synchronous performance) and again would lead to more boilerplate.

No matter which approach we agree upon for a codebase, it needs to be consistently adopted and applied across the board, this is not a good area to mix and match ideas.

Propagating errors through callbacks

We’ll be talking about the “errback” pattern in detail in the next post, but here’s an example of propagating an error from an asynchronous function without throwing:

function asyncOp(input, cb) {
  if (!Array.isArray(input)) {
    return cb(Error('input needs to be an array'));
  }
  doAsyncThing({input: input}, cb);
}


asyncOp('oops', function (err) {
  if (err) {
    //do something
    return; //exit the function
  }
});

This way the caller of asyncOp can decide what to do with the error, the right thing may be to throw, or the right thing may be to send a 500 response to a client, or simply log it and move on – it all depends on context.

Emitting errors

Event emitters are suitable for cases where we want to centralize a variety of asynchronous activity into a single object.

We’ll discuss Event Emitters in more detail in a later post, however here’s a quick example of emitting an error:

var EventEmitter = require('events').EventEmitter;

function makeNotifier(opts) {
  var ee = new EventEmitter;

  connectToPubSub(opts, function (err, pub) {
  if (err) { return ee.emit('error', err); }
    pub.on('message', function (msg) {
      ee.emit('notification', {type: 'message', data: msg});
    });

    pub.on('error', function (err) { 
      ee.emit('error', err); 
    });
  });

  return ee;
}

Ordinarily we would actually inherit from EventEmitter but for terseness we instantiating directly from it in the example.

Event emitters flatten structure of error handling, instead of passing errors up through several callbacks we just emit them from one object.

Rejecting promises

Another asynchronous construct is Promises. We’re going to talk exclusively about EcmaScript 6 promises since these are now the standard. EcmaScript 6 Promises are available in Node.js v0.12 and io.js and can be polyfilled with the lie module in Node.js v0.10.

To propagate errors from promises, call the reject method.

function asyncOp(input) {
  return new Promise(function (resolve, reject) {
    if (!Array.isArray(input)) {
      return reject(Error('input needs to be an array'));

    }

    doAsyncThing({input: input}, function (err, val) {
      if (err) { return reject(err); }
      resolve(val);
    });

  });
}

asyncOp('oops')
  .then(function (val) { 
    //yay
  }) 
  .catch(function (err) {
    //boo
  });

While a promise represents a future value, a deferred represents a future operation. The native Promise.defer method can be used to create a less nested function that does the same thing:

function asyncOp(input) {
  var deferred = Promise.defer();
  if (!Array.isArray(input)) {
    return deferred.reject(Error('input needs to be an array'));

  }

  doAsyncThing({input: input}, function (err, val) {
    if (err) { return deferred.reject(err); }
    deferred.resolve(val);
  });

  return deferred.promise;
}

Promises might not be for everyone, the syntax doesn’t appeal to some, and promises have a reputation for being heavy on the CPU. Nevertheless evidently they are here to stay. The potential of native promises opens doors for performance, optimizations and even at that it comes to CPU cycles for an abstraction around an asynchronous operation CPU usage is less of a concern – since the bottleneck tends to be the asynchronous operation. Later on we’ll be talking about Promises and throwing in greater detail.

Why to not try-catch

JavaScript is a dynamic asynchronous language but try-catch is a synchronous construct originally designed for compiled languages.

When it comes to JavaScript, the try-catch is something of a square peg in a round hole, which can lead to developer confusion.

For instance, consider the following:

try { 
  setImmediate(function () { throw Error('Oh noes!'); });
} catch (e) {
  console.log('Gotcha!', e);
}

The error won’t be caught at all. The setImmediate function is an asynchronous operation so the throw happens after the try-catch block has been executed.

Creating functions that expect the try-catch pattern can lead to poor code quality. Beyond documentation or reading source code, we have no way to know whether a function could throw an error. Typically functions that may throw can work fine most of the time, which makes it very easy omit the try-catch – until that one time when the process crashes. Or else the opposite can occur, a super defensive style of programming where we try-catch every single call (believe me, it’s not sustainable).

This aside, the requirement to try-catch at runtime demands significant hoop-jumping in v8 (Node.js JavaScript engine). Use of try-catch causes runtime de-optimizations resulting in unnecessarily slow execution – particularly for hot code paths. This means if we adopt throwing and try/catch all over a codebase it could significantly affect performance.

When try-catch may be useful

Having said that, there are cases where try-catch could be necessary.

Typically if our app is converting a JSON string into a JavaScript object, that data would have arrived from an external source.

No matter what the external source we should consider the input as untrusted (even if it’s our own database, since it’s possible that some other part of the system allowed unchecked user input into the database.)

Parsing JSON

Unfortunately, the native JSON.parse will throw when given invalid JSON. Yet the malformed JSON isn’t necessarily a programmer error it may be the result of malicious user manipulation, a problem in another part of the stack (e.g. the UI), or simply corrupted data, perhaps due to some hardware fault.

Unless we know for certain our JSON is valid, we need to try-catch JSON.parse to avoid unwanted crashes:

try { 
  JSON.parse(userJson); 
} catch (e) { 
  console.warn('Bad user input', userJson, e);
}

It would be trivial to execute a Denial of Service attack on a Node.js process that doesn’t try-catch JSON.parse – simply keep sending invalid JSON.

Third-party Code

Similarly if we’re using a module that throws unnecessarily we may have to use a try-catch. The mustache module, for example, has a policy of throwing on template parser errors. This seems okay because it’s responding to incorrect developer input. Except that it’s not critical to the system that the template renders. Should we really have to crash the server because someone made a typo in a template? In this case we’d need to wrap certain mustache calls in a try-catch (or switch to a template module that doesn’t throw).

Syntax Support Detection

Another place where try-catch may be useful as we move into the brave new world of EcmaScript 6 is feature detecting syntax.

EcmaScript 6 is the first update to JavaScript that actually adds new syntax – there’s no way to polyfill the behaviour.

Say we wanted to detect the support of ES6 let:

var letSupport;
try { 
  Function('let foo = 1;')(); 
  letSupport = true; 
} catch (e) { }

var mod = letSupport ? 
  require('es6mod') : 
  require('transpiledMod');

There may however be better (as yet unknown) ways than using try-catch.

Isolating try-catch

If a try-catch absolutely must be used, isolating the try-catch in a separate function and calling it from the original function confines de-optimizations to the purpose built function.

function parse(json) {
  var err;
  try { json = JSON.parse(json); } catch (e) { err = e; }
  if (err) {return err;}
  return json;
}


function doThings(json) {
  //doing things...
  json = parse(json); //doThings wont be affected by try-catch in parse
  //doing things...
}

Throwing in promise callbacks

Promises automatically catch exceptions.

For instance instead of calling reject we could throw:

 

function asyncOp(input) {
  return new Promise(function (resolve) {
    if (!Array.isArray(input)) {
      throw Error('input needs to be an array');
    }
  });
}

asyncOp('oops')
  .catch(function (err) {
    //handle the error

  });

However, let’s not forget that throw is synchronous, so the second throw in the following code would not be caught:

function asyncOp(input) {
  return new Promise(function (resolve) {
    if (!Array.isArray(input)) {
      return throw Error('input needs to be an array');
    }

    doAsyncThing({input: input}, function (err, val) {
      if (err) { throw err; }
      resolve(val);
    });
  });
}

Calling reject is advised over throwing. The reject function will trigger the catch callback in both synchronous and asynchronous contexts.

When we call then on a promise, a new promise is returned.

If the callback passed to then contains a throw the promise returned from then will trigger its catch callback if one is assigned.

Like so:

asyncOp
  .then(function () {
    throw Error('oh no');
  })
  .catch (function (err) {
    //handle it
  });

Again this won’t work in asynchronous scenarios:

asyncOp
  .then(function () {
    setImmediate(function () {
      throw Error('oh no'); //causes an unhandled exception
    });
  })
  .catch(function (err) {
    //wont trigger
  });

As with calling reject in the Promise instantiation callback it’s advisable to stay away from throw in promises and instead be explicit about error propagation in both synchronous and asynchronous context:

Asynchronous example:

asyncOp
  .then(function () {
    var defer = Promise.defer();
    setImmediate(function () {
      defer.reject(Error('oh no'));
    });
    return defer.promise;
  })
  .catch(function (err) {
    //will trigger
  });

Nesting asynchronous actions in Promise handlers isn’t a good pattern however. Prefer to encapsulate the asynchronous operations in separate promises, pass them to Promise.all:

Promise
  .all([asyncOp, asyncOpWithError])
  .catch(function (err) { /* will trigger */  });

An alternative to throwing synchronous errors in then callbacks is to generate a (synchronous) promise that triggers an error state with Promise.reject:

asyncOp
  .then(function () {
    return Promise.reject(Error('oh no'));
  })
  .catch(function (err) {
    //will trigger
  });

Since promises catch exceptions, we can use the Promise.reject methods counterpart, Promise.resolve, in place of try-catch when try-catch would usually be a necessity (e.g. with JSON.parse):

Promise.resolve('{"badJSON":oops}')
  .then(JSON.parse)
  .catch(function (err) {
    //JSON is bad.
  })

But there is a caveat. Promises catch all exceptions. So bear in mind registering a catch method on a promise will prevent the process from dying even when syntax and other native errors occur inside a promise context.These will probably be ignored in our catch handlers as the JavaScript engine typically (and rightly) causes a crash when these errors occur. So this means, native errors in a promise context can and do lead to silent failure.

We can handle this by detecting native errors and re-throwing them:

function rethrowNativeError(err) {
  var native = err instanceof TypeError ||
    err instanceof SyntaxError ||
    err instanceof EvalError ||
    err instanceof RangeError ||
    err instanceof ReferenceError;

  if (native) { throw err; }
}


asyncOp
  .then(function () {
    throw Error('manual')
  })
  .catch(function (err) {
    rethrowNativeError(err);
    //handle the manual error
  })
  .then(function () {
    undefined.badness();
  })
  .catch(function (err) {
    rethrowNativeError(err);
    //app will have thrown by now
  });

It’s not ideal but it can prevent some serious bugs and lost development time.

A nice approach to this would be to create a new constructor that inherits from the Promise constructor (call it Promise2 for example) and then implicitly rethrow native errors.

Unfortunately this can’t be done, native ES6 Promises cannot be inherited from.

What about try-catch-finally?

Using the finally block can mitigate clean-up issues, however it’s not discussed in detail here because we encourage the use of alternatives to try-catch for reasons presented.

What about the uncaught exception event?

Uncaught exceptions can be caught like so:

process.on('uncaughtException', function () {
  //recover?
});

Beyond enhanced logging this is an ill-advised practice. Any time uncaughtException is used, there should be a process.exit inside the event handler function.

An unhandled exception means your application – and by extension Node.js itself – is in an undefined state. Blindly resuming means anything could happen.

What about domains?

Since Node.js 0.8 the core domains module has allowed for an asynchronous try-catch-like mechanism.

The idea is to group code into “domains” and catch unhandled errors. Sounds good, but we still have the same issues as resuming execution after uncaughtException, just compartmentalized.

This is why domains are designed to be used alongside the cluster module. Each child process is housed inside a domain, when an error is caught the child process is restarted.

Until Node.js 0.12 (and io.js), both domains and cluster were experimental (and in the cluster module’s case, actually kind of broken).

This is an area where development and deployment converge.

The typical approach is to use process management tools and error logs – particularly in the absence of reliable clustering in core. This way some of the same deployment tools used for scaling horizontally can be used for scaling vertically.

We like to use nginx and nscale as part of our devops tooling instead of domains and cluster, choosing to make process management more of a declarative configuration affair rather than a programmatic one.

Nevertheless, now that cluster and domainhave moved to “unstable”, (which means the implementation is solid, but the API may change), we may see more usage of these in production.

Conclusion

To summarise, it’s not that throwing and try-catch are inherently dangerous, but as a pattern used in an asynchronous, stateful imperative language they do make it too easy to inject instability into a process. It’s much safer to simply restart the process than to try and continue.

Endeavor to keep throwing to a minimum. Reserve it for scenarios where the intent is to explicitly exit the process, and prefer to delegate this decision to application level when possible.

If the purpose is to propagate an error, send an Error object via callback, emitter, promise. For synchronous functions, returning an Error is an option, but consider other approaches such as using typically asynchronous constructs like callbacks or promises for synchronous operations or creating an API contract that houses errors and values in a united object. Most importantly, pick an option and be consistent.

Endeavor to avoid try-catch, if it must be used (because something else is throwing) isolate it in a separate function.

That’s all for this time, hope you enjoyed it.

Let us know in the comments if you found it worth the read, or if you vehemently disagree I’d love to open a dialogue!

The next tip is titled ‘Reproduce Core Callback Signatures’, see you then!

Subscribe to this blog to be notified as soon as follow-on posts are published.

Want to work for nearForm? We’re hiring.


Email hello@nearform.com

Twitter @nearform

Phone +353-1-514 3545

Check out nearForm at www.nearform.com.


By: David Mark Clements

David Mark Clements is a JavaScript and Node.js specialist based in Northern Ireland. He is nearForm's lead trainer and training content curator. From a very early age he was fascinated with programming. He first learned BASIC on one of the many Ataris he had accumulated by the age of nine. David learned JavaScript at age twelve, moving into Linux administration and PHP as a teenager. Node has become a prominent member of his toolkit due to its versatility, vast ecosystem, and the cognitive ease that comes with full-stack JavaScript. David is author of Node Cookbook (Packt), now in its second edition.
  • https://github.com/briandipalma Brian Di Palma

    Are you sure `Promise.defer` is in the standard? I can’t find it in the draft https://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-constructor

    • https://github.com/briandipalma Brian Di Palma

      Also you can subclass Promise, I imagine what you are referring to is that node might not yet have support for it?

      e.g. https://esdiscuss.org/topic/when-the-only-tool-you-have-is-subclassing-all-extensions-look-like

      • David Mark Clements

        As far as I can see from that discussion there’s certainly no clean way “subclass” or inherit from Promise, the conclusion of the discussion suggests using a function that returns a new promise (which isn’t inheritance in the sense that one constructor inherits from the other). The function then sets the prototype of the instance to the prototype faux-constructor (the initialiser function). This means it mostly works like a promise, but, vitally, isn’t called with `new`. This means you can’t subclass Promises and throw them into code as a drop-in replacement.

        In terms of actually subclassing, if you have io.js installed you can try this:

        node –harmony_classes –use-strict -e “class P extends Promise{}; new P”

        You’ll see the output,

        [eval]:1
        class P extends Promise{}; new P
        ^
        TypeError: # is not a promise
        at new P ([eval]:1:1)
        at [eval]:1:28
        at Object.exports.runInThisContext (vm.js:54:17)
        at Object. ([eval]-wrapper:6:22)
        at Module._compile (module.js:410:26)
        at evalScript (node.js:476:25)
        at startup (node.js:95:9)
        at node.js:868:3

        You can also try inheriting the desugared way, in Chrome, Node 0.12 or iojs

        function P() { return Promise.apply(this, arguments) };
        P.prototype = new Promise(function(){})
        new P(); //Uncaught TypeError: # is not a promise

        Any variety of the more sophisticated approached to prototypical inheritance also fails.

        If I’m missing something here I’d love to know, because it would be cool if we could subclass promise to rethrow syntax errors in catch

        • https://github.com/briandipalma Brian Di Palma

          From the spec https://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-constructor

          `The Promise constructor is designed to be subclassable. It may be used as the value in an
          extends clause of a class definition. Subclass constructors that intend to inherit the specified
          Promise behaviour must include a super call to the Promise constructor to create
          and initialize the subclass instance with the internal state necessary to support the Promise and
          Promise.prototype built-in methods.`

          I’d be wary of relying on output from an engine that doesn’t yet have classes enabled by default (indicating that there might be some deviations from the spec). When I try

          > iojs –use-strict -e “class P extends Promise {}; new P(function(){})”

          With the latest iojs there are no errors.

          • David Mark Clements

            oh wow look at that, I should have checked the latest io.js (when I wrote the first draft that wasn’t out).

            Still not able to inherit in the traditional way in io.js:

            function P() { Promise.apply(this, arguments); }
            P.prototype = Object.create(Promise.prototype);
            new P(function(){}); //TypeError

            Thanks for bringing this up, I’ll add it into the article when I’m able

    • David Mark Clements

      Hey @BrianDiPalma1:disqus your absolutely right, it looks like Promise.defer has been deprecated https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred

  • http://glebbahmutov.com/ Gleb Bahmutov

    Like the advice, but I would start using promises right away – makes the error handling much cleaner than callbacks. Also, I would worry about starting the promise chain, so that any error in the first promise-returning function is handled in the `.catch` callback. See http://glebbahmutov.com/blog/starting-promises/ blog post how to start the promises.

    Another point about promise chains – if the promise chain is not `.done` the promises can be quietly swallowed, see http://glebbahmutov.com/blog/why-promises-need-to-be-done/

    • David Mark Clements

      Hey @glebbahmutov:disqus as ever thanks for contributing! Good point about `done`. Promises are great, I like them, I hear a varied opinions on them from fellow devs. It does make error handling cleaner, but the trade off (as mentioned), is if you forget to handle *any type of error including syntax errors* in a catch you get a silent failure

      • http://glebbahmutov.com/ Gleb Bahmutov

        yeah, I usually start promise chain with `Q().then(…)` and always finish with `.done()` to avoid this. Or use bluebird that throws exception if unhandled.

  • http://twitter.com/davej Dave Jeffery

    Try/catch will be back with a vengeance in ES2016 because of async functions. It’s quite a nice pattern and you can use it now in latest node/iojs thanks to generators and a simple spawn function: https://gist.github.com/jakearchibald/31b89cba627924972ad6

  • Robin Venneman

    Interesting article, in the deferred example, I think it will not work to return deferred.reject() directly. This will throw a TypeError “Cannot read property ‘then’ of undefined”, unless it rejects or resolves and returns the deferred.promise at the end of the function.

  • Adrien Laugueux

    Good article, have you considered using reactive programming with frameworks like Reactive-Extensions RxJS ? https://github.com/Reactive-Extensions/RxJS

  • markstos

    > “Beyond enhanced logging this is an ill-advised practice. Any time uncaughtException is used, there should be a process.exit inside the event handler function.”

    Are you sure? I’m using both the `bugsnag` and `newrelic` modules, as it’s useful for both services to know about uncaught exceptions. If either one included “exit” in their handler (neither does), it would be impossible to gather metrics for both services.

    Put another way, it doesn’t seem safe for one ‘unCaughtException’ handle to exit unless it knows that it is the only handler. Otherwise, the ‘exit’ could prevent other legitimate handlers from running.