What is the difference between using the Nodejs Q promise library's .finally() and .done() statements.
For example whats the difference between these two?
Q(...)
.then(...)
.finally(); //or fin()
Q(..)
.then()
.done();
promise.done(onSuccess, onError) simply allows you to process resolved value. An additional benefit is that does not imply any error swallowing (as it is the case with promise.then()), it guarantees that any involved exception would be exposed. It also effectively ends the chain and does not return any further promise.
promise.finally(fn) is for registering a task that must be done after a given promise resolves (it doesn't matter whether promise succeeds or fails). Usually, you use it for some kind of cleanup operations e.g. imagine you set up a progress bar, that needs to be hidden after the request is done (no matter if it was successful), then just do promise.finally(hideProgressBar). Additionally promise.finally() returns input promise, so you can return it for further processing.
The difference is in chaining and error handling, and error logging:
Q(...)
.then(...)
.finally();
Here, if the then throws, the finally will still run, but no error will log. In Q finally is run regardless of the .then being successful or not. This is like the finally keyword in JS try/catch/finally. It is also possible to chain additional thens to the chain in this case.
Q(..)
.then()
.done();
Here, done indicates that the promise chain has ended, you can not chain to it any more. If you pass it only an onFulfilled handler it will not run if the then threw, and it will log errors if it ends with an exception.
Related
When working with promises in JavaScript, we have a possibility to use .then, .catch and .finally. Each of these methods returns a new Promise object.
Using .then is pretty straightforward - we chain them. The use case for finally - is to put it at the end of the chain of .then and .catch. But
In the code below, as I understand, we initialize promise p which can resolve or reject. I could use .then(resolutionHandler, rejectionHandler), which would be self-explanatory as it's only 1 .then "handler" with both handlers, but in case of sequencing .then and .catch instead of the latter approach-
**Are .then and .catch handlers somehow paired and treated like .then(resolutionHandler, rejectionHandler)? or something else is happening? **
const p = new Promise((resolve, reject) => {
reject("ups...");
});
p
.then(data => {
// this is success handler for Promise "p"
})
.catch(err => {
// Is this failure handler for Promise "p"?
})
Not exactly. When you have p.then(handleThen).catch(handleCatch), if p rejects, handleCatch will handle it. But handleCatch will also handle errors thrown by handleThen.
Although those sorts of errors are pretty unusual with if handleThen contains only synchronous code, if handleThen returns a Promise, handleCatch will be able to handle that Promise if it rejects.
<somePromiseChain>
.catch(handleCatch);
will have the handleCatch handle any errors thrown anywhere in the above Promise chain.
In contrast, with p.then(resolutionHandler, rejectionHandler), the rejectionHandler will only handle a rejection of p. It will completely ignore anything that occurs in resolutionHandler.
If the resolutionHandler is completely synchronous, doesn't return a Promise, and never throws (which is pretty common), then .then(resolutionHandler, rejectionHandler) is indeed equivalent to.then(resolutionHandler).catch(rejectionHandler). But it's usually a good idea to use .then(..).catch(..).
With p.then(success).catch(fail), the .catch is actually attached to the Promise returned by the .then - but if p rejects, the .then's Promise rejects as well, with the same value as p's rejection. It's not that the catch is attached directly to p, but it's attached to a .then which passes p's rejection through.
Every .then / .catch is attached directly to the upper Promise it's called on, but sometimes that upper Promise passes through the value from its upper Promise unaltered. That is. p.then(undefined, fail).then(success) will run success if p resolves, with the intermediate .then passing the resolution through. With rejection, p.then(success).catch(fail) will run fail because the .then(success) passes the failure from p through unaltered.
Calling catch internally calls reject
As per mdn
The catch() method returns a Promise and deals with rejected cases only. It behaves the same as calling Promise.prototype.then(undefined, onRejected) (in fact, calling obj.catch(onRejected) internally calls obj.then(undefined, onRejected)).
Consider the following:
function listFoo() {
return $.ajax(...);
}
function listFooey() {
return $.ajax(...);
}
function parallelLoad() {
//Note that we do *not* return the promise here.
$.when(
listFoo(), listFooey()
)
.then (function(fooResponse, fooeyResponse) {
console.log('then')
//doStuff
});
}
//Kick everything off and log
console.log('before parallelLoad');
parallelLoad();
console.log('after parallelLoad');
A typical output on the console would be (jsfiddle) :
before parallelLoad
after parallelLoad
then
The first two functions return the ajax Promise, so the reference to it is not lost. However, the parallelLoad() function does not return the promise, so when it is executed, it creates the promise, returns immediately, and the promise would appear to immediately go out of scope.
In practice, I have not seen a problem arise from this pattern, but I'm wondering if that's just "luck".
In the example above, is parallelLoad() "safe" in that (assuming the ajax calls run without server/network errors) the then clause will always execute?
Or is there a race condition where sometimes the ajax calls might not even be initiated or the promises might be garbage collected before the then clause can run?
(Currently I am using jQuery 3.3.1)
In general, promises that are not going to be resolved and are not longer needed can be garbage-collected before they are resolved.
This is not the case in your example however. Even while there is no obvious global variable holding onto the promise, it still is referenced from the running ajax requests. The requests created by listFoo() and listFooey() keep the callbacks installed on them alive, as they need to run them when they finish. In turn, these callbacks reference the promises (or at least, the deferred parts of them) and the callbacks installed on them, as they need to resolve the promises and call the callbacks when they run. In turn, these callbacks reference the promise (or deferred) created by the $.when call and the callback installed on it, as they still are needed until everything is settled. Only once both requests are done and all callbacks are executed, everything will be garbage-collected.
No, it can never happen that a promise will miss to invoke your callbacks because something got garbage-collected at the wrong time.
There's no problem there, the then will always execute.
One main obvious downside with this is since the parallelLoad promise is lost, there is no continuation and you cannot chain any other events to the success or failure of listFoo, listFooey.
I think the promise chaining style may be the cause of the confusion.
Look at it this way, if you rewrite parallelLoad like so (breaking the chain),
function parallelLoad() {
//Note that we do *not* return the promise here.
var whenPromise = $.when(listFoo(), listFooey());
whenPromise.then(function(fooResponse, fooeyResponse) {
console.log('then');
//doStuff
});
}
You can now probably easily see that the then part is simply just another line of code(with a callback) waiting to be called and so will not be garbage collected until it is done.
After the then gets called, parallelLoad will go out of scope for you, but the references to the promises and callbacks created therein are still held internally in memory until the promises are resolved/rejected and callbacks are called.
Nathan, Closures are about identifiers right? So parallelLoad() references both listFoo and listFooeyidentifiers (as functions) creating two closures. Those closures will remain in place until (at least) each of those functions complete and resolve. That means that parallelLoad() is the Execution Context and has not completed yet. The next step in the process is the .then() in response to the resolve.
That means the Execution Context (parallelLoad()) is established at the parallelLoad() call and does not change until all the code in the .then() has completed, it is a safe paradigm.
Using promisified request module, i want to get the body attribute of the response. Not sure if I should use a Promise try.
So which one is correct here ?
A. Without Promise.try
request.getAsync("http://example.com")
.then( x => x.body )
.tap( x=> console.log('body:',x) )
B. With Promise.try
request.getAsync("http://example.com")
.then( x => Promise.try( () => x.body ) )
.tap( x=> console.log('body:',x) )
After the first element of a promise chain, an error is guaranteed to throw down the chain's error path and become available to be caught by a .then(null, errorHandler) or .catch(errorHandler).
Therefore there is no point using Bluebird's Promise.try() other than to get a chain reliably started. Doing so will just bulk up your code and make it less efficient.
How about inner chains?
This is more interesting. Inner chains are commonly used to allow access to earlier results via closure. Flattened chains lose this ability.
Consider :
request.getAsync("http://example.com")
.then( x => {
return untrusted.method().then((y) => other.method(x, y));
});
The inner chain has its own starter, untrusted.method(), which may cause failure in one of two ways:
it may throw.
it may return a non-thenable.
A throw is unavoidable (without addressing untrusted.method()), though the thrown error will be catchable in the outer chain.
But a non-thenable can be guarded against. You can write :
request.getAsync("http://example.com")
.then(x => {
return Promise.try(untrusted.method).then((y) => other.method(x, y) );
});
Now Promise.try() ensures the inner chain has a reliable start. Promise.try(untrusted.method) is guaranteed to be thenable.
As a bonus, an error thrown by untrusted.method() is now catchable in the outer chain or the inner chain, which can be useful, particularly for error recovery.
Promise.resolve(untrusted.method()) would similarly protect against a non-thenable being returned but would not allow a synchronous throw to be caught within the inner chain.
DEMO: untrusted() throws
DEMO: untrusted() returns value, not promise.
So in summary,
within a promise chain, there's no value in wrapping synchronous expression(s) in Promise.try().
within a promise chain , there's no value in wrapping trusted, asynchronous function/method calls in Promise.try().
within a promise chain, there is potentially great value in wrapping an untrusted function/method call in Promise.try() to guarantee an inner chain's reliable start.
Not sure if I should use a Promise.try.
No, you should not. In a promise then callback, all synchronous exceptions will be caught and lead to a rejection of the result promise, there is nothing you need to do for that. No try/catch statements, no Promise.try invocations. They would just be unnecessary fluff.
Promise.try should only be used at the start of a promise chain, where you are not yet in a promise callback.
The definition of this Promise.try is:
Start the chain of promises with Promise.try. Any synchronous
exceptions will be turned into rejections on the returned promise.
So it depends by the implementation of request.getAsync method. Does it throw any exception?
If the answer is "no", you do not need to use Promise.try.
If the answer is "yes", you can use Promise.try if you need to catch the error thrown by request.getAsync as rejected promise. In this case, you can wrap the
request.getAsync inside Promise.try:
Promise.try(() => {
return request.getAsync("http://example.com");
}).tap(x => console.log('body:', x.nody));
Please note that if the answer is "yes" but do not want to catch the exception as a rejected promise, you do not need to use Promise.try.
I'm watching this video(code around 36 minutes) on Promises, but I'm confused by a bit of code:
getUser('mjackson')
.then(getTweets)
.then(updateStatus)
.then(undefined, handleError);
My problem with understand this is why is the last then called? And why does it pass undefined as the first argument?
If getTweets() fails, then updateStatus() is never called. Which is why I'm confused as to why the last then is called if the second (the one that contains updateStatus()) isn't.
I know handleError is a callback, I just don't get why undefined is passed.
I hope that makes sense.
Any then with a function provided as the second parameter will catch and handle any rejected promises farther up the promise chain.
So if getUser or getTweets or updateStatus fails, the error will be handled in handleError.
This code is passing undefined as the first parameter because in this case updateStatus is the last thing that the person writing it wants to do, and there's nothing more to do if that succeeds.
Most promise libraries, and the ES6 promise standard provide a .catch(...) method that is really just a shorthand for .then(undefined, ...):
getUser('mjackson')
.then(getTweets)
.then(updateStatus)
.catch(handleError);
There are two ways of using the promise methods, for async flow control, particularly for error-handling.
The first is:
anOperation.then(onsuccess, onerror);
The second is:
anOperation.catch(onerror);
If onsuccess is undefined or null in the first case, it means that there is no success option there (so it would move to the success after that one, in the success case).
Most promises you see/use are .then(onsuccess, undefined).
It should be noted:
If you throw an error in onsuccess, the onerror does not get fired. It moves to the next error down. It is impossible to fire both callbacks in the same .then, it's an either-or scenario
If you return a value from a .catch or an onerror callback, it is expected that you have solved the issue, and thus, you go into the next onsuccess of the next .then, with the value you returned. If this isn't what you want, rethrow, or return a Promise.reject
The signature of the then function is:
p.then(onFulfilled, onRejected);
If you pass undefined as the first parameter, it behaves as p.catch(onRejected);
In a then chain, every success functions will be called in an order unless one failed. If one failed, all the execution will break unless a second parameter of a then exists. The second parameter will behave the catch of any.
If a then function returns a promises, you can make a then chain.
handleError has called, because one of the previous promises in the chain was failed.
Promises in a chain with an example
Here my JsBin to understand a promise chain. Try to change giveMeSuccessPromise and giveMeFailPromise functions in doX or catchX functions.
In a chain of then clauses as below:
p.then(doFirst)
.then(doSecond)
.then(doThird)
.then(undefined, catchFirstSecondThird)
.then(doFourth)
.then(doFifth, catchEveryThing);
In this example, in case of any of the doFirst, doSecond or doThird failed catchFirstSecondThird will be executed. In case of any of the catchFirstSecondThird, doFourth or doFifth failed catchEveryThing will be executed.
The order of the execution in happy path:
doFirst, doSecond, doThird, doFourth, doFifth
The order of the execution when only doFirst fails:
doFirst, catchFirstSecondThird, doFourth, doFifth
The order of the execution when only doSecond fails:
doFirst, doSecond, catchFirstSecondThird, doFourth, doFifth
The order of the execution when both doFirst and doFourth fails:
doFirst, catchFirstSecondThird, doFourth, catchEveryThing
Resources:
From MDN, Promise API
From MDN, API of then
From MDN, API of catch
A very good tutorial about promises
I have a pretty long chain of check in Q, and I would to interrupt it when an error rise:
I have looked to How to abort a failing Q promise in Node.JS and other answers on SO, but it seems impossible to me, that can't exist nothing else.
Example
`
Q().then(function(){
return Q.ninvoke(myObject, 'save');
}).fail(functon(err){ // if error
res.status(400).send(err.message);// like duplicate key
}).then(function(){
add object another object to db referenced to myObject
}).fail(functon(err){ // if error
res.status(400).send(err.message);// like connection error
}).then(function(){
do somethinng else
}).done()
`
Obviously, if it can't save the first object, I would not go on through other steps, but I would exit without throwing error and blocking the execution of the server without sending the message to the client.
I tried to add two function to done(OK, REJECTED), but only OK() is called.
I would like to avoid chunking the code in 3 different functions if possible.
As far as I can gather, you don't need to do anything particularly tricky, just understand how success and failure propagate through a .then chain, then exploit this knowledge to get the behaviour you desire.
the chain will continue down the "success" path as long as you keep on having success
the chain will continue down the "fail" path as long as you keep on having failure
if a "success" function throws an error or returns a rejected promise, then the chain will continue down the "fail" path
if a "fail" function returns a value or a fulfilled promise, then the chain will continue down the "success" path.
This behaviour is at the heart of the Promises/A+ standard.
If you were to write your chain with a single, terminal .fail, then (if I understand correctly) you would get the behaviour you seek.
Q().then(function () {
return Q.ninvoke(myObject, 'save');
}).then(function () {
add object another object to db referenced to myObject
}).then(function () {
do somethinng else
}).fail(functon (err) {
res.status(400).send(err.message);
});
Here, failure at any point in the .then chain will cause the propageted promise to drop straight through the rest of the .thens and be caught by the terminal .fail.
This has the advantage over the code in the question, which has multiple interwoven .fails. That pattern can be made to work but you need to introduce a mechanism to suppress multiple "fail" behaviour once the failure has been handled. In unusual circumstances that might be appropriate, but in general it would be regarded as messy.
The only other thing you might need in the fail handler is to distiguish between the different types of failure and act accordingly. That's pretty trivial as the formal variable err can be tested, eg in a switch-case structure. You appear not to need such a test as the action on failure is the same in all cases.