aaaidan aaaidan - 7 months ago 38
Javascript Question

How do I debug promise-based code in node?

I am using Cujo's great When library to provide a Promises/A+ implementation for my Node project, although this question is not node-specific.

Generally, When's great: it lets me write more maintainable, readable code.

However, when my callbacks fail unexpectedly (accessing a property of a null variable, etc), the exceptions are effectively swallowed by When, as seems to be specified by the Promises/A+ spec. Unfortunately, this means I don't get any feedback about the error (other than the callback stops executing at that point). No error type or message, no line number.

To illustrate:

// hypothetical asynchronous database query
database.query(queryDetails).then(function(result) {

var silly = 3.141592654;
silly(); // TypeError: number is not a function!

process(result); // this code is silently never executed

});


I can think of a handful of (unacceptable) ways to tackle this:


  • providing failure callbacks for every
    then
    call (to dump the reason/exception to the console)

  • wrapping all callback bodies in try-catches

  • littering the codebase with "landmark logs" ala
    console.log('I got here 123')



Am I just doing it wrong? Surely I'm not alone in finding the debuggability of promises-based code poor. Is there an obvious solution I'm missing?

Answer

Ok, so summing up the information from the comments and add some.

  • There is nothing in the Promises/A+ specification that dictates how to deal with this problem. The specification is about the minimal requirements for good interop between different promise libraries - so one promise library can consume promises created in another and vice versa.
  • Several libraries solve this problem by including a .done method that explicitly declares the chain has ended, this causes uncaught rejections to be thrown. Libraries like When and Q solve the problem this way. For example if your .then after .query was a .done you'd get a long stack trace.
  • Newer, less naive implementations of promises like Bluebird solve this problem by automatically figuring out possibly uncaught rejections and logging them out for you loudly. They also give you a hook. When has experimental support for this using the monitor.

As such:

 require('when/monitor/console'); // when will now log async rejections used with
                                  // `then` , this is experimental in when.

With bluebird

Promise.longStackTraces(); // Bluebird always logs async rejections but with this 
                           // option it will stitch the asynchronous context stack
                           // for you in your methods.
  • ES6 promises' behavior is unspecified on this. There is no explicit requirements on the native implementations with regards to this. However, I know vendors are working on it and I'd expect engines to start figuring this out even in native implementations.