In addition to then(), Q.js also has a done(). done() is usually called at the end of a promise chain, like this:
promise
.then(callback)
.then(callback)
.done(callback);
This will catch any rejections that were not handled by the previous then()s, and it will handle any exceptions raised in then()'s callbacks.
Is there something similar in when.js? How do you handle exceptions raised in callbacks? And what if you never register a rejection handler?
It looks like when now has .done() as well as .catch() and .finally().
See https://github.com/cujojs/when/blob/master/docs/api.md#extended-promise-api
As far as I know, there is no done in when.js. Indeed, if you read the last paragraph dedicated to debugging, there is a mention on a tool called monitor, which:
[...] monitors promise state transitions and then takes action, such as logging to the console, when certain criteria are met, such as when a promise has been rejected but has no onRejected handlers attached to it, and thus the rejection would have been silent.
There's no done in when.js.
I advice to request it in their issue tracker, and for a meantime use other library that provides done.
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.
In the situation that we do not deal with reject, must we include reject in the promise executor?
for example:
new promise ((res)=>{
res(a);
})
If you know that a promise will never be rejected, adding a rejection handler is not necessary. E.g. it's obvious this will never be rejected and adding a rejection handler would be plain silly:
Promise.resolve("abc")
.then(result => console.log(result));
In all other cases, not providing a rejection handler is pretty much the same as not wrapping an error-throwing piece of code in try-catch. It might be intentional, of course, but bear in mind that node.js will treat unhandled promise rejections as if they were uncaught errors: by terminating the node.js process. That's what it says on the console when an unhandled promise rejection occurs:
(node:7741) 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.
So, summing up:
skip rejection handlers when you know for a fact a rejection is impossible,
skip rejection handlers when you require the application be terminated upon rejection (in future versions of Node.js),
write rejection handlers in all other cases.
EDIT (the question became clearer after the edits were made, hence my update to the answer):
As for including the reject() call in the promise executor, no, you do not need to add it. Again though, you have to make sure that the internal logic guarantees that resolve() will always be called.
It's not necessary, no, but it's not a very elegant way to handle errors - if an error is thrown whilst your promise is pending, you'll want to take some other action and reject the promise, letting the user know what's wrong.
Have a look at MDN Promise Docs, they explain the concept of handling errors in Promises pretty well!
I'm trying to make my Express app handle all requests as similarly as possible. I had a server that handled everything with callbacks and the pyramid of doom was terrible. It was also difficult to ensure that I was sending a response for all the calls.
So I'm migrating to a Promise based system (sequelize, which utilizes bluebird's promise library).
I've read that it's best to always follow up with a .catch(function(err){ report it } call.
Does .done() accomplish the same thing?
My thought (might be naive) is that I can handle all res.status(x).send(y) calls in the .done(success, failure) functions.
Will failure catch everything that catch() would? Is it wrong to send all responses in the done() function (all success messages in success and all failure/error messages in failure?
Edit - I'm finding out that sequelize uses bluebird's promise library, so done() has been depricated for finally(). But, finally() doesn't take parameters in the functions, so it doesn't seem like I can send all data to finally() and handle it there.
done() is deprecated and I recommend you don't use it - its goal was to throw the rejection as an exception so it propagates. Bluebird keeps track of unresolved promises on its own so then does this and warns anyway.
You do not want to do .catch(err => { /* handle */}) unless you actually have the ability to handle the exception - just like catch (e) {. If you don't have anything meaningful to do with the exception don't do anything.
In routes for example, you can return a 500 error status if an exception has occured and report something to your tools and to the client.
finally() is meant primarily for resource cleanup and for making sure you leave everything in a clean state.
Native Promises do not have a .done() method. I would recommend sticking with the ECMAScript spec and just using .then(success).catch(failure) this will ensure that any error that might get thrown in your success function still get caught and handled by your failure function.
The .finally(function() handler) -> Promise function is slightly different. It gets invoked for both success and failure, but notice that it doesn't get any data as a parameter for the handler.
It's intended to be used for cleanup, just like a normal finally block in a try...catch.
If you need access to the value returned by the Promise, use .then():
.then(
[function(any value) fulfilledHandler],
[function(any error) rejectedHandler]
) -> Promise
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();