I am jumping onto a node.js project where some errors fail silently and the app doesn't throw any errors unless I put it in a try..catch block. For example:
console.log(ok) // doesn't throw any errors during runtime; the api just fails silently.
try {
console.log(ok)
} catch(e) {
console.log(e)// throws ReferenceError: ok is not defined
}
Does anyone know why this might be?
Thank you in advance!
Does anyone know why this might be?
Clearly, code in a containing scope is catching and suppressing errors. Which is an unfortunate, but common, practice.
Sadly you'll just have to root out that code, there's no real shortcut. You could run with --inspect (on recent versions of Node) and tell the debugger to stop on all exceptions, even caught ones, which may help you track them down some. But it's going to be tedious, I'm afraid.
One possible further thought: On only-slightly-older versions of Node, unhandled Promise rejections are not reported. So if that code is inside (say) a Promise executor (the function you pass new Promise) or a then or catch handler (or an async function), the throw is being converted to a promise rejection. The latest versions of Node report unhandled Promise rejections. (In the not-distant-future, they'll also terminate the Node process.)
Related
Coming from a python async background, in python it's very important to always keep track of async tasks (promises). The runtime gives errors for "floating" async tasks with no references. In Javascript, though, in some cases it seems perfectly OK to just start an async task and not await it or even remember its Promise, for instance if you don't care about its return value and just want it to get executed "later".
Are there guidelines or best practices about when in JS/TS, either in browser or in node.js, it's acceptable to just let an async task go, and not keep a reference to it? Clearly if you care about its return value in the mainline code you have to await it, and similarly if you care that errors are reported before the main function completes. What are other cases and considerations?
To be clear I'm not asking about opinions, I'm asking what are the important things to keep in mind when "firing and forgetting" async tasks (if that's the right term).
Whether and when to do this will be a matter of opinion, so that part is off-topic for Stack Overflow.
But the concrete part is the question of: if you do this, are there any precautions you have to take? And the answer there is: Yes. You need to catch and handle errors.
Consider this fire-and-forget async call that returns a promise:
doSomethingAsync();
If the thing being done can fail, that promise can be rejected. If it's rejected and nothing handles that rejection, then:
In a browser, you get an error written to the browser console. 99.9999999% of users won't notice.
In Node.js, you get this error written to the console:
(node:26477) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Note that warning: At some point, a future version of Node.js may start *terminating the process when you allow a promise rejection to go unhandled.
So if you fire-and-forget, be sure to catch errors, even if you just silently swallow them:
doSomethingAsync().catch(error => {}); // Obviously only do this if you really don't
// care about the error!
Fun side note: In JavaScript, you have unhandled promise fulfillment all the time. Consider:
doSomethingAsync()
.then(result => {
// ...do something with result...
})
.catch(error => {
// ...handle/report the error...
});
Are all the promises handled there?
No. And that's just fine.
Remember that then and catch create and return a promise. The promise returned by catch in the above has no handlers attached to it. That's fine, provided it is only ever fulfilled, not rejected. Nothing ever does anything with that fulfillment. :-)
I think the general concern I would have is, do you have a mechanism to handle errors?
eslint has a no-floating-promises rule that at the very least forces you to add a .catch(), which I think is good.
I understand when the UnhandledPromiseRejectionWarning error happens.
What I don't understand is why the the spec was written in such a way as to allow this behavior.
For example, the following code is bad, if callApi() rejects than we get the UnhandledPromiseRejectionWarning error in Node a similar error in each browser:
class A {
constructor () {
this._stuff = Promise.reject(Error('oops'))
}
getStuff () {
return this._stuff
}
}
const a = new A()
setTimeout(() => {
a.getStuff().catch(err => console.log('I caught it!'))
}, 100)
But why?! I tend to thing of promises as abstracting over when stuff happens.
That's exactly how the promise acts if callApi() is successful: the caller of getStuff() will get the stuff when the data is available.
But the rejection case doesn't work the same way. What I'd expect to happen (if I hadn't been burned by this before) is for the burden of handling the error to fall to the caller of getStuff. That's not how it works, but why?
The detection of an unhandled promise rejection is imperfect in node.js. I don't know exactly how it works internally, but if you don't have a .catch() in a promise chain and that promise rejects, even if you assign it to a variable and then later add a .catch() to it, then it will give you the "unhandled rejection warning".
In your specific case, it is probably because you got back to the event loop with a rejected promise that wasn't caught - your .catch() is only added later on a subsequent event. There's no way for the node.js interpreter to know that you're going to do that. It sees that an event finished executing and a rejected promise was not handled so it creates the warning.
To avoid the warning, you will have to code it differently. You don't really show a real world use so we can see what you're trying to accomplish to make a specific suggestion. As with other types of things like this, the best way to handle it varies depending upon the real world use case.
If you're asking why an unhandled rejection is a warning at all, then that's because it can be a serious error and if there was no warning, then it would simply fail silently and the developer would never be the wiser. It's very much akin to an unhandled exception in synchronous code. That's a serious error and aborts the program. node.js has been notifying for awhile that an unhandled rejection may get the same treatment in the future. For now, it's just a warning.
The word "possibly" suggests there are some circumstances where you can get this warning in the console even if you catch the error yourself.
What are those circumstances?
This is pretty well explained in the docs:
Unhandled rejections/exceptions don't really have a good agreed-on
asynchronous correspondence. The problem is that it is impossible to
predict the future and know if a rejected promise will eventually be
handled.
The [approach that bluebird takes to solve this problem], is to call a
registered handler if a rejection is unhandled by the start of a
second turn. The default handler is to write the stack trace to
stderr or console.error in browsers. This is close to what happens
with synchronous code - your code doesn't work as expected and you
open console and see a stack trace. Nice.
Of course this is not perfect, if your code for some reason needs to
swoop in and attach error handler to some promise after the promise
has been hanging around a while then you will see annoying messages.
So, for example, this might warn of an unhandled error even though it will get handled pretty well:
var prom = Promise.reject("error");
setTimeout(function() {
prom.catch(function(err) {
console.log(err, "got handled");
});
}, 500);
Introduction
This question aims to, eventually, resolve a problem I'm having in development with Bluebird. However, I'm also using the opportunity to get some things clarified, so there will be side-questions. I also apologize in advance for any feelings of confusion or boredom you might experience while reading the story to come.
Questions
As far as my understanding goes, Bluebird attempts to intelligently catch ignored rejections, according to the following strategy:
The second approach, which is what bluebird by default takes, is to call a registered handler if a rejection is unhandled by the start of a second turn.
-- Bluebird Readme # Error Handling
Now herein lies the first side-question: What does "the start of a second turn" mean?
Later in the same section, the following is documented:
Of course this is not perfect, if your code for some reason needs to swoop in and attach error handler to some promise after the promise has been hanging around a while then you will see annoying messages. In that case you can use the .done() method to signal that any hanging exceptions should be thrown.
-- Bluebird Readme # Error Handling
Now, I believe that I ran in to the situation described above with my use case being as follows:
I call a function which will provide me the promise to which I attach a .catch():
lib.loadUrls()
.catch(function(e){console.log(e);});
Internally, that function loads content from URL1 and based on the content, loads content from URL2 in sequence:
lib.loadUrls =
return this.loadUrl1()
.then(this.loadUrl2.bind(this))
If the second promise in this chain is rejected the error is handled by the catch first, and then by Bluebirds Possibly unhandled error handler as well.
This last behavior is unwanted and I can not figure out why it's doing this. So question two could be: Why, despite an error handler being attached and executed, does Bluebird still consider the possibility of the error being "unhandled"?
I'm thinking, apparently the promise "has been hanging around for a while" by the time the rejection propagates to the .catch(). In which case I should solve it (according to the quoted documentation) by "using the .done()".
Now, I've tried several things, but I can't quite figure out how to "use the .done" in this scenario. (It doesn't help that .done() returns undefined, preventing me from .finally-ing.)
So this introduces my third, and fourth questions: How do I use .done() in this scenario, and how do I explicitly conclude a promise-chain, but still attach a .finally()
EDIT 1: I've created some JSFiddles to reproduce the bug:
Using Bluebird 1.0.5 reproduces the bug.
Using the latest Bluebird.js reproduces the bug (at this time)
Using Beta version 0.10.0-1 does not reproduce the bug.
EDIT 2: The dev fixed the bug.
This was indeed just a regression bug in bluebird and is now fixed.
The bit about needing to use .done() is pretty much theoretical, you won't run in a situation in practice where you would need to attach error handlers in such a way that would cause false positives to be reported.
It's most likely Bluebird bug, as handled error should not be reported (assuming you properly handle promises in loadUrls body). So probably you should report it to Bluebird issue tracker.
Concerning done, it's pure access function that's best if used instead of then or catch when you just process resolved value.
It's good to treat done as first choice function and use then and catch only if you really need transformation to other promise, with such approach you also don't need to rely on buggy errors monitoring (best to turn it off completely).
In your case done should be used as:
lib.loadUrls().done(); // eventual error will be thrown
and if for some reason you want to handle error specifically (e.g. in running server you don't want it throw) do:
lib.loadUrls().done(null, function (error) {
// handle error
});
EDIT:
Just noticed, that you still want to process promise returned by lib.loadUrls().catch(..) with finally. In such case done is not a solution. done should only be used as final call, but you can combine it with finally as follows:
lib.loadUrls().finally(function () {
// cleanup
}).done();
The Problem
JSFiddle:
http://jsfiddle.net/missingno/Gz8Pe/2/
I have some code that looks like this:
var d = new Deferred();
d.resolve(17);
return d.then(function(){
//do some stuff...
})
.then(function(){
var obj = a_funtion_that_returns_null_on_IE();
var x = obj.some_property; //BOOM!
});
The problem is that when I am on IE all I can see are 'obj' is null or not an object errors, without any reference to the corresponding line number and without the debugger halting at the offending line (like I wish it would).
This kind of issue is making the code a pain to debug and the only solutions I can think of right now (messing around with the control flow library or resorting to step-by-step debugging with the debugger or console.log) are things I would rather not have to do.
What I think is going on
In order to allow errbacks to be added after the chain is fired, then will preemptively catch any exceptions thrown by the callbacks. I think this is the reason for the IE debugger not halting on the error or showing the usual error message witht the line number in it.
The error messages without the line numbers are coming from the control-flow library: it provides a deferredOnError hook that is called whenever an exception is caught and saved for later and the default behaviour is console.error-ing the Error object:
dojo.config.deferredOnError = function(err){
//a chance to log the exception after it is captured by "then"
//or do other things with it
console.error(err);
}
Sadly, I could not figure out a way to get the line number or stack trace from the error object in IE and the hook is called in a way that does not allow me to just rethrow the exception and let it bubble up to the toplevel.
What I want
I want to have a better way to debug the async code then goind along with the debugger step-by-step. In the best case a way to have the debugger halt on the exceptions (as it does on unhandled exceptions) or at least a way to get line numbers or stack traces from the Error object that was thrown.
This works with any framework without prior configuration and all recent browsers support this.
Pause On Caught Exceptions: This will actually stop javascript's execution and will take you exactly where the problematic code is, as it's happening.
In Chrome:
Developer Tools,
Sources tab,
Pause on exceptions (stop-like icon) and then
the Pause On Caught Exceptions checkbox
What I ended up doing
I added a sequencing function to my mini library of async helper functions. It basically runs a sequence of "then" calls, except that it adds extra intermediate steps to rethrow any exceptions that end up being caught by the Deferreds. It also accepts an optional error handler to catch the exceptions.
When I call it it looks like this:
go([
function(){
return 17;
},
function(x){
//return some stuff
},
function(){
var obj = a_function_that_returns_null_on_IE();
var x = obj.some_property; //BOOM!
}
], function(){
//an optional error handler
});
Another reason that I did things this way is that I have lots of code that needs to work simultaneously with either sync or async code (using Deferred.when to do the chaining). Using my custom function let me use a single, unified syntax and the errors not being captured in the async case is consistent with the sync case, where there are no Deferreds involved. I also think its OK to not capture the error, since unlike in the general case, when I am using "go" I know a-priori what code will be called, so there is no need to capture exceptions for if someone needs to catch them in the future.
Also, using a custom solution gave me the freedom to enforce some personal design preferences :)
In addition to that, I ended up reducing the amount of exceptions I generate myself throughout the code base. Managing exceptions in async code is more annoying then usual and sometimes its simpler to just fallback into handling error conditions by returning null or an error code.
Also, we made sure that any exceptions that we create ourselves are instances of the builtin Error class, intead of other objects. The reason is that the builtin Error class records the line number and stack trace of where it was generated in a kind of cross-browser way.
2016 solution
If you're using native ES Promises do absolutely nothing; Chrome automatically reports uncaught promise rejections in the console.
Notice how the caught one (Second fail) doesn't show anything but the uncaught rejection appears in the console after the code is done running.