How to debug javascript promises? - javascript

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?

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.

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)

*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.

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).

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);
});

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)=>{

Related

Getting unhandled exception from a reject within a promise while debugging in VS Code

Is it normal that I get an unhandled exception error from a reject(new SomeKindOfError()) within a promise while debugging in VS Code, but not when I'm not debugging? Or is there something wrong from my code structure?
From what I have learned from several tutorials about promises and stackoverflow answers, a Promise#catch() at then end of a promise chain is enough to catch a rejection that might occur in the chain. But why is it still marked as an unhandled exception by the debugger?
Here's the structure I used:
function returnAPromise(): Promise<any> {
return new Promise<any>((resolve, reject) => {
// do something here
if (isConditionMet) {
resolve()
} else {
reject(new SomeKindOfError()) // debugger breaks here
}
})
}
someElement.onSomeEvent(() => {
// only care about the errors that might occur
returnAPromise().catch((error) => {
if (error instanceof SomeKindOfError) {
// perform necessary actions when this error occurred
}
})
})
P.S. I already tried out debugging without performing break when an unhandled exception is encountered but it kind of defeats the purpose of using the debugger: to check out for unhandled exception that might occur.
Edit:
Also, I tried calling returnAPromise() without a catch and that one prints a warning in the debugger console saying rejected promise not handled within 1 second.
Thanks #Bergi for pointing out that it's because of the Error constructor. I did not notice that... :'(
It turns out, the problem is not because of the promise not catching it. It's because of an error in extending built in Typescript classes.
Here's a stackoverflow post I found, which refers to this Typescript documentation about breaking changes in v2.1.
Extending built-ins like Error, Array, and Map may no longer work. As part of substituting the value of this with the value returned by a super(...) call, subclassing Error, Array, and others may no longer work as expected. This is due to the fact that constructor functions for Error, Array, and the like use ECMAScript 6's new.target to adjust the prototype chain; however, there is no way to ensure a value for new.target when invoking a constructor in ECMAScript 5. Other downlevel compilers generally have the same limitation by default.
Because of this, I have to add an additional line of code in the constructor of the class that extends the Error class.
class SomeKindOfError extends Error {
constructor(m: string) {
super(m)
// I have to add this additional line of code
Object.setPrototypeOf(this, SomeKindOfError.prototype)
}
}

Why does my ES6 Promise Rejection need to be consumed immediately to avoid a console error message?

Please note: The following is an issue that behaves differently on different browsers. So perhaps this is a browser implementation issue. I would love some advice regardless.
In my application, I am creating a couple of promises that I may not be consuming until quite some time in the future. Which should be fine, they are promises, after all.
If the promise being stored away is resolved, there is no issue. I can consume it as far in the future as I want, and as many times as I want. As expected.
If the promise being stored away is rejected, however, there is an issue. Unless I consume that rejection shortly after it is made (not sure how shortly) a console message will crop up in Chrome or Firefox indicating that there is an uncaught promise rejection/error. IE does not pop up that error.
So consider the following code:
console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
console.log("Promise rejection made.");
Note that there is no use or consumption of the promise foo. It is merely stored away for some future use.
The console on IE looks like this:
About to make a promise rejection.
Promise rejection made.
Which is expected. However, the same code on Chrome will yield the following console:
About to make a promise rejection.
Promise rejection made.
Uncaught (in promise) Error: Foo Rejected Promise.(…)
Firefox looks pretty much like Chrome, other than the wording around the "uncaught" error.
But the thing is that I intend to handle this "error" much later, at the time I am consuming the promise. Merely HAVING a rejected promise should not cause a console error..... that should happen if I consume the promise and don't handle the error.
To simulate a "much later" handling of the error, consider this alteration of the code:
console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
console.log("Promise rejection made.");
setTimeout(() => {
foo.catch((err) => {
console.log("Displaying " + err.message + " 10 seconds later.");
});
}, 10000);
Now in this case, we "handle" the error by displaying something on the console after a timeout. Now IE still handles this as I would expect:
About to make a promise rejection.
Promise rejection made.
Displaying Foo Rejected Promise. 10 seconds later.
In this case, Firefox does like IE, and displays exactly these messages, and does not give me an erroneous console error.
Chrome, however, still gives the erroneous error:
About to make a promise rejection.
Promise rejection made.
Uncaught (in promise) Error: Foo Rejected Promise.(…)
Displaying Foo Rejected Promise. 10 seconds later.
So Chrome both complained about my error not being handled, and then displayed that it was handled.
It appears that I can get around all this with the following code that that seems like a hack. Basically I do a "fake" handling of the error when the promise is created and then really handle it the way I want to later:
console.log("About to make a promise rejection.");
var foo = Promise.reject(new Error("Foo Rejected Promise."));
foo.catch(() => { }); // Added this hack-ish code.
console.log("Promise rejection made.");
setTimeout(() => {
foo.catch((err) => {
console.log("Displaying " + err.message + " 10 seconds later.");
});
}, 10000);
But this is ugly code.
My question is twofold - is there some way of looking at what Chrome (and to some extent FireFox) is doing and think of it as a favor? Because to me it seems awful. And secondly, is there a better way of getting around this than the hack of pretending to handle an error when you aren't?
Thanks in advance.
Any promise that has a chance to be rejected, should be handled, similarly to exceptions.
There are not so many chances that a promise won't be chained synchronously. But if it won't, it requires 'hack-ish' .catch(() => { }) to synchronously handle the rejection.
Chrome promise rejection behaviour puts this
var foo = Promise.reject(new Error("Foo Rejected Promise."));
foo.catch(() => { }); // Added this hack-ish code.
setTimeout(() => {
foo.catch((err) => {
console.log("Displaying " + err.message + " 10 seconds later.");
});
}, 10000);
into the same error-handling boat as
try {
throw new Error("Foo"));
setTimeout(() => {
try {
throw new Error("Foo"));
} catch (err) {
console.log("Displaying " + err.message + " 10 seconds later.");
}
}, 10000);
} catch (err) {}
Wouldn't the exception wait 10 seconds to be caught by next try...catch before being thrown? It won't wait even a single tick. It requires 'hack-ish' try { ... } catch (err) {} block to perform as intended.
This behaviour is already well-known to Bluebird library users for quite a long time. Bluebird has adjustable error handling and allows to debug promises efficiently even on the large scale.
The behaviour is also known in Angular 2 development, it is forced for promises with Zone.js library.
Since promise debugging has limited value and applicable to development environment, it is possible to modify or disable this behaviour for native promises in production builds:
if (typeof DEBUG_PROMISE === 'undefined') {
window.addEventListener('unhandledrejection', (e) => {
e.preventDefault();
console.warn(e.reason);
});
}
Here's more reading on the subject.
As someone who has worked with promises for a couple of years now, I understand why this behavior is set in Chrome. it is easy to forget to catch a rejected promise, and if there is an error in .then(), there will be no notification and your program will just stay silent blankly. It can be very hard to debug.
function throwException(c){ return a.b = 2 ;} //a is not defined. throws exception.
let p = q.resolve("a")
.then(x => x + "b")
.then(y => throwException(y))
.then(z=> z+"c"); //this will say nothing in Q library
this can easily create a very obscure behaviour when one forgets to catch the error.
In Q library, you have the .done() function to actually enforce what Chrome is doing here:
let p = q.resolve("a")
.then(x => x + "b")
.then(y => throwException(y))
.then(z=> z+"c")
.done(); //this will throw an exception.
So, it is a known problem in promises. Chrome have simply decided to make .done() the default behavior, asking you to explicitly .catch(_=>{}) if you want to ignore failed promises. Q has decided to make silent failure default, asking you to explicitly use .done() if you want an exception on failed promise. If you like the latter approach, you can always include a promise library. It is a matter of taste IMHO.

Crashing the application from within a Q promise instead of propagate a rejection through a chain of promises?

So basically the problem is that Q swallows my exceptions that are not meant to reject the promises, but to crash the application as early as possible, so that I know what exactly is broken.
I know that I can (and probably should) use the done method at the end of the chain, but it's really a pain in a butt to keep track of the chain ends. And it doesn't help much because it doesn't prevent Q from catching it and once cached the exception looses it's stack trace.
Is there a way to crash early when the exception is thrown rather than propagate the rejection up the chain hoping there is done at the end of it?
Right, so my PR for Q just got merged, you can now do this:
process.on("unhandledRejection", function(err, promise){
throw err; // terminate with error if a `.catch` is not attached
});
Which will cause the process to exit whenever an exception is not explicitly handled (via catch). This ends a long saga of debugging issues with promises. Gone are the days of .done.
Just make sure to get Q 1.30, released 6 minutes ago :D
You're looking for process.exit(1) (or any other exit code), which will terminate the process immediately without throwing an exception.
One way to accomplish this is to defer the actual execution of the promise callback. This will force the function into the event queue and the calling function will not catch this error.
An example:
try {
setTimeout(function () {
throw new Error('Not caught');
});
} catch (e) {
console.log(e);
}
However, I'm not sure if this is much better than just using .done.

Function Returns a Promise, Check Errors

I have a function doSomething() that returns a promise chain, utilizing the Q framework. The contents are similar to something like:
loadDataSet : function (params) {
return Q.fcall(function() {
//Do Something
})
.then(function(){
//Do Something Else
throw New Error('unexpected error');
});
}
The calling code goes something like:
var promise = loadDataSet(args);
I want to figure out whether that error was thrown. Notice, in the loadDataSet function implementation, I did not utilize the .done() function.
So far, I have code that looks like this and have been unsuccessful in capturing the error and handling it appropriately (here, the code is modified, slightly from above):
try {
loadDataSet(args)
.catch(function(error) {
return error
})
.done();
}....
The goal is to handle the error from a try-catch block. What am I missing?
Well, this is going to a be a bummer.
You can't
While a lot of promise libraries let you do this and will report unhandled rejections for you - in Q you have no methods to automatically detect these failures.
You have to Use .done or change a promise library. Heck, even native promises are going to be able to do this in a few days.
Q specific solution:
In Q your only realistic option is to use .done, unlike then done is not throw safe and you can throw exceptions from there and they won't be suppressed - this requires you to remember to always terminate chains with done but it works:
myObj.loadDataSet(handleSuccess, handleError).done(); // will throw on rejection
Personally until Q fixes this and other issues I cannot recommend using it to anyone.
Modern libraries and native promises
I've written a specification based on work by Domenic and Petka for promise libraries to be able to report errors globally and hook to them. Several libraries already implement this including bluebird and when. Domenic is working on a parallel specification for web browsers.
Currently supported or are going to be supported in the next few weeks are: bluebird, when, es6-promise, rsvp and native promises in io.
// log any unhandled promise rejections
process.on('unhandledRejection', function(reason, p){
console.log("Possibly Unhandled Rejection at: Promise ", p, " reason: ", reason);
// application specific logging here
});
As for browsers, something like:
window.addEventListener("unhandledrejection", function(e) {
var reason = e.detail.reason;
var promise = e.detail.promise;
console.log("Unhandled rejection", promise, reason);
});
This protocol is less supported but there are plans to include it in native promises. Currently Firefox native promises will report unhandled rejections and Chrome's will attempt too - but there are no browser hooks for it yet (it's coming though).
Note that teams are working on very interesting tooling. After a discussion with Paul Irish I'm convinced great things are going to come our way in terms of tooling for debugging promises in browsers that will make native promises almost as debuggable as bluebird promises (which is awesome!).
You can't throw exceptions inside then as no one will able to catch it. Instead, create a new Q.deferand call reject on it whenever there's an error
loadDataSet : function (params) {
var deferred = Q.defer()
Q.fcall(function() {
//Do Something
}).then(function(){
//Do Something Else
deferred.reject('error message')
}, deferred.reject)
return deferred.promise
}
then use it like this
loadDataSet().then(function (data) {
//ok, got data
}).catch(function (err) {
//error!
})

How to catch promise runtime Javascript errors?

I'm currently implementing a PDF viewer based on PDF.js and as part of that I learned about promise objects.
I also learned that runtime errors are not automatically shown in the debugging console:
PDFJS.getDocument(...).then(
function(pdfDocument){
alert(UndefinedVariable); // Not shown in console!
},
function(error){
console.log("Error occurred", error);
}
);
I haven't been able to find a pretty way to show runtime errors in the promise functions, other than adding .done() as described in http://www.asyncdev.net/2013/07/promises-errors-and-express-js/ (which doesn't work for PDF.js) or adding .catch(function(error){ console.error(error); }).
I know that I can break on exceptions from runtime errors in the debugger, but I also get breaks on other exceptions (in jQuery) by doing so, which means I have to step thorugh 5 jQuery exceptions on every page load, before I can even check if my own code contains runtime errors.
Is there any way to force the promise functions to log runtime errors like normal (without writing extra code for every function call)?
The problem you're experiencing is that an exception in the then callback does reject the promise returned by .then(), instead of calling the error handler that you passed in. That will only trigger for errors in the promise on which you called .then(). So you can chain your handlers:
PDFJS.getDocument(...).then(function(pdfDocument){
alert(UndefinedVariable); // Now shown in console!
}).then(null, function(error){
console.log("Error occurred", error);
});
Here, then(null, …) could also be abbreviated by catch(…).
If there is no done method that throws on errors, you could implement it yourself like this by throwing in a setTimeout.
Is there any way to force the promise functions to log runtime errors like normal (without writing extra code for every function call)?
No. That's just not how they were designed.
In the Promise implementation, there is a try ... catch which takes an error from the callbacks and turns it into an error returned by the Promise.
One thing you can do is change that try...catch to log the errors before invoking the failure of the promise.
https://github.com/mozilla/pdf.js/blob/master/src/shared/util.js#L936
} catch (ex) {
console.error(ex); // <--- add this line
nextStatus = STATUS_REJECTED;
nextValue = ex;
}
If native ECMAScript 6 promises are used instead, this trick probably won't work.

Categories