82

I am trying to understand how to debug asynchronous code that is based on promises. By Promises I mean ECMAScript 6 based promises and by debugging I mean using the built-in chrome or firefox debugger.

What I am having trouble with - is that when an error occurs I can't seem to get the stack trace no matter how I 'reject' it.

I tried these:

console.log(new Error('Error occured'));
throw new Error('Throwing an Error');
return new Error('Error returned by the onRejected function');
reject(new Error('Pass Error to the reject function'));

But none of those returns the actual error in the code, or the stack trace.

So my question is - how to properly debug javascript Promises?

YemSalat
  • 19,986
  • 13
  • 44
  • 51
  • Based on http://www.html5rocks.com/en/tutorials/es6/promises/ I'd rather do: ```reject('Error')``` but can you post a jsfiddle so that we have something concrete to work with? – Renra Sep 13 '14 at 20:40
  • 3
    This is one of the major advantages of working with the Bluebird promises library. It captures stack traces for you anytime you have an unhandled rejection or exception in your promise code. I've been using it with node.js and it is saving a TON of time. I honestly don't know how to get that same functionality with built-in promises. – jfriend00 Sep 14 '14 at 00:35
  • 2
    @Renra please don't give bad advice - throwing (or rejecting with) primitives is a nasty habit that makes debugging much much harder. – Benjamin Gruenbaum Sep 14 '14 at 07:59
  • https://stackoverflow.com/questions/43834559/how-to-find-which-promises-is-unhandled-in-nodejs-unhandledpromiserejectionwarni – Bergi Nov 03 '17 at 03:42

6 Answers6

64

This is a great topic to discuss, the sad news are this is actually quite hard with native promises.

Debugging raw ES6 promises in Chrome is horrible. This is because they will silently suppress errors and whenever you omit a catch it will not give you any indication that the promise failed. Update: Chrome now logs unhandled rejections (see this link for how)

 Promise.resolve("foo").then(function(){
      throw new Error("You will never see this");// silent failure
 });

In Firefox, things are a bit better since they perform unhandled rejection detection - however, it's still flakey and if you assigned the promise anywhere it won't work.

So, what can be done?

Include Bluebird - it's a superset of ES6 promises and you can swap it right inside, it has a richer API, it's faster and it has amazing stack traces. It's built with debugging in mind and includes great error handling facilities.

Once you've included Bluebird, call:

Promise.longStackTraces();

Which will slow it down a bit (it'll still be very fast) and will give you amazing error messages. For example:

Promise.resolve().then(function outer() {
    return Promise.resolve().then(function inner() {
        return Promise.resolve().then(function evenMoreInner() {
            a.b.c.d()
        });
    });
});

In native promises - this will be a silent failure and will be very hard to debug - with Bluebird promises this will show a big red error in your console by default giving you:

ReferenceError: a is not defined
    at evenMoreInner (<anonymous>:6:13)
From previous event:
    at inner (<anonymous>:5:24)
From previous event:
    at outer (<anonymous>:4:20)
From previous event:
    at <anonymous>:3:9
    at Object.InjectedScript._evaluateOn (<anonymous>:581:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:540:52)
    at Object.InjectedScript.evaluate (<anonymous>:459:21)

Once you're done debugging - you can swap it out and go back to native promises. Personally I value knowing I have errors in production so I don't recommend it but it's certainly doable.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 1
    What probably slows it down is the fact that it must label each line number in your code for when the stack is being made, which would make longer code slightly more. Though for debugging, this is definitely worth the slowdown. – Neil Sep 14 '14 at 08:26
  • 2
    Well, it needs to stitch the stack traces together but only on errors - generally Bluebird is very fast and the performance is still very good with long stack traces on the client side. – Benjamin Gruenbaum Sep 14 '14 at 08:31
  • Thanks a lot for your answer, at least now I know I'm not the only one who's struggling with this. Will be definitely looking into Bluebird. Also, will try to make a JSFiddle to illustrate that no errors are displayed when the promise failed. And besides that - a promise where the error occurred may actually show its status as 'resolved', I think this will happen if the promise is not the last one in the chain. – YemSalat Sep 14 '14 at 14:52
  • What about the un-nested equivalent of the code in this answer, where `outer`, `inner`, `evenMoreInner` would become `first`, `second`, `third`? It's not self-evident whether or not the stack trace would include `first` and `second`. – Roamer-1888 Sep 17 '14 at 02:09
  • 1
    BLUEBIRD! Awesome. Wow. I was using Q and super frustrated. – RandallB Mar 27 '15 at 20:29
  • I'm using bluebird and this makes no difference in the lack on info on bluebird error. – BentOnCoding Apr 13 '15 at 18:01
  • @BentOnCoding are you sure you turned on long stack traces? – Benjamin Gruenbaum Apr 13 '15 at 18:57
  • Now it seems that recent Chrome throws "Uncaught (in promise)" – spirytus Jun 08 '15 at 11:54
  • 1
    Yes, Chrome actually has a great promises debug panel coming :) In canary soon, will update then – Benjamin Gruenbaum Jun 08 '15 at 11:56
  • For anyone stuck with plain ES6 Promises, you can get [long stracktraces like this](https://gist.github.com/joeytwiddle/8c357b8a4ac6803a0f188d495901b6bc) – joeytwiddle Aug 09 '16 at 17:31
  • @BenjaminGruenbaum Does Chrome have this promises debug panel now? I can't find it. – Flimm Aug 24 '16 at 08:56
  • It ended up not being too useful so it got removed. Unhandled rejection tracking is pretty good and you can use a library like bluebird which has monitoring. – Benjamin Gruenbaum Aug 24 '16 at 09:59
14

This answer is an addition to Benjamin Gruenbaum's answer: If you use a catch statement in the promise chain, you'll get the stack trace by error.stack:

        Promise.longStackTraces();

        function outer() {
            return Promise.resolve();
        }

        function inner() {
            return Promise.resolve();
        }

        function evenMoreInner() {
            a.b.c.d()
        }

        Promise.resolve()
            .then(outer)
            .then(inner)
            .then(evenMoreInner())
            .catch(function (err) {
                    console.log(err.message);
                    console.log(err.stack);
                });

Error Message:

ReferenceError: a is not defined
at evenMoreInner (test/test_promise.js:58:17)  <<<< HERE's the error!
at Context.<anonymous> (test/test_promise.js:64:23)
Matthias M
  • 12,906
  • 17
  • 87
  • 116
  • 2
    Thank you, was missing the `stack` property and was wondering where it went, haha! – NiCk Newman Mar 08 '16 at 02:34
  • 4
    Just to reiterate that point... I was fooled by the fact that `console.log(err)` does not include err.stack. You need `console.log(err.stack)` to see the details. – Chris Dennis Mar 23 '16 at 12:26
  • It didn't work for me until I ticked the "async" checkbox as mentioned in goat's answer. http://stackoverflow.com/a/25827268/247696 – Flimm May 11 '17 at 15:26
13

*This doesn't directly answer your question, but it may be helpful nonetheless.

Chrome devtools recently got a new feature that's useful for debugging async code, such as Promises.

http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/

Basically, enable the "async" checkbox in the sources tab, and Chrome will reconstruct the call stack for you as if it were synchronous code.

Screenshot

goat
  • 31,486
  • 7
  • 73
  • 96
  • 1
    It still doesn't print errors to the console and does not provide any info on where the error occurred. By errors I mean things that you're not 'planning' to catch, e.g. type errors that might occur within the promise, etc. – YemSalat Sep 13 '14 at 20:21
  • a type error should show up in the console, even in a promise. – dandavis Sep 13 '14 at 22:44
  • 4
    @dandavis no it will not. – Benjamin Gruenbaum Sep 14 '14 at 08:04
  • 11
    Google Chrome now supports Async call stacks by default and has removed the checkbox: https://developers.google.com/web/updates/2017/05/devtools-release-notes#step-into-async – Dan Salo Sep 21 '17 at 15:28
1

They seem to be working with debug tools in Chrome. See this thread for more info.

https://code.google.com/p/v8/issues/detail?id=3093

I havn't checked if this is already in the dev version or beta version but I hope it will be soon. It might then be included in the normal version in January 2015 or so (just a personal guess, absolutley no promise since I don't even work for Google).

  • I don't see any debug tools specific to promises in Chrome now. Do you? – Flimm Aug 24 '16 at 09:46
  • 1
    Google removed the feature a couple of months ago. Try the async helper instead. [see this commit](https://chromium.googlesource.com/chromium/src.git/+/559a35b9574ea1e17b2d6ccc5bc4f0cc955bb570) [About async debugging](https://www.html5rocks.com/en/tutorials/developertools/async-call-stack/) – Johan Bergens Sep 29 '16 at 12:11
1

The best way to debug promised is to listen to unhandledRejection event of your process.

For example, here is how you might set it up and dump a stack trace...

 process.on('unhandledRejection', (reason, p) => {
   console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
   // Stack Trace
   console.log(reason.stack);
 });
ra9r
  • 4,528
  • 4
  • 42
  • 52
  • 1
    Where would you put that code ? I have an error in a promise and cannot find even in which file to look into... – Stephane May 20 '18 at 17:24
1

Inside .then() function when the Promise object is returned,
add a console.log() statement.
Do the same inside .catch() if needed:

...
  .then(
  (docs) =>{
      console.log(docs); ==> put here
      resolve(docs);
  })
  .catch((e)=>{
julianm
  • 2,393
  • 1
  • 23
  • 24