Having a really difficult time understand Promise chaining - javascript

(I think I learned this, it is that .then returns a promise even if it is not run, i never knew this, thank you)
Please forgive me if this is a dumb question but I simply don't get this, I've googled this many times but those articles only talk about Promise very briefly like how to create one, how to resolve, how to .then and stuff.
I'm confused about promise chaining.
This is my code:
let p = new Promise(function (resolve, reject){
let value = 19;
if(!value){
resolve(value)
} else {
reject(value)
}
})
p.then(x => {console.log(x*x); throw false})
.then(null, x => console.log(x))
//19
This code prints 19 and I have no clue why?.
I do know every .then returns a promise but in this case, p.then shouldn't have even run because the promise is not resolved. Now in a way, it seems that didnt run but if so, why did the second .then run?
Our first .then didn't run, it didnt run any promise whatsoever, so nothing should have displayed.
Now unless 2nd .then is working the same as p.then() which I hope it doesnt because that will only create more confusions, I dont understand why will the 2nd .then run?.
I'm sorry again if my question or english bad but I'm really curious about this.

The .then function takes two functions as parameters, one which is called when the Promise resolves and one when it rejects. It returns a new Promise which gets resolved or rejected with whatever value the function which was executed returns (or throws). If no handler was executed, the Promise will just resolve or reject with the same value the Promise .then was called on.
In your example the first .then call only received one argument, and as the Promise rejects it won't be executed. Therefore the Promise it returns will also reject, and thus the second argument will be executed.
// some examples
Promise.resolve()
.then(called, notCalled);
Promise.reject()
.then(notCalled, called);
Promise.resolve(1)
.then(null, notCalled)
.then(it => it + 1, notCalled)
.then(called, notCalled);
Promise.reject(1)
.then(notCalled, null)
.then(notCalled, it => it + 1)
.then(called, notCalled);

A Promise can be in one of these states:
pending -> initial state, neither fulfilled nor rejected.
fulfilled -> the operation was completed successfully.
rejected -> the operation failed.
In your code sample, the promise is being rejected (since if value is a truthy condition).
The then method accepts two arguments:
p.then(onFulfilled[, onRejected]);
p.then(value => {
// fulfillment
}, reason => {
// rejection
});
Since the then is chainable, the reject get's fired on the second chained then which is the line of code:
x => console.log(x)
Hence, printing the value 19.

Looking at your questions on the two answers above (and also, perhaps the fact that you didn't selected either answer as correct), I wonder if you're still confused about this.
Here is an explanation based on stepping through your code:
p.then(x => {console.log(x*x); throw false})
.then(null, x => console.log(x))
Reading one token at a time:
p is a promise. It has the code as you defined for p: let p = new Promise ...
We immediately call p's then function. This runs the code in p (since p has not yet been run -- promises only run once). Your code in p is a bit confusing because it defines value to be 19, but then it says if ( !value ) ... which means "if value has no value (i.e., is falsey)". In your cause value does have a value, 19. So that causes the else part to run, which says reject(value). This moves p from an "undetermined" state to a rejected state.
Back in your main code, the then clause (the first one), only has one argument. A then clause typically has two arguments, one for code to run if the promise resolves (succeeds), and one if it fails. In your case you only provided one argument to your first then call. Since p was rejected, JavaScript doesn't run the code in the first then, but (as you point out from your learning in bold) it does still return another promise.
Now that the promise from the first then has been determined, we invoke the chained (second) then. Jonas Wilms points out the key fact: If no handler was executed, the Promise will just resolve or reject with the same value the Promise .then was called on. So that means the second then gets the same result as the first then, rejected.
Since the second then has two arguments, JavaScript runs the second one (the one for the rejection case), which prints 19.

Related

Promise.race to keep evaluating until it gets a non-rejected promise?

I race requests in my system and want to return whoever is the fastest. However, the fastest promise could also technically throw an error despite the slower promises being successful.
How can I race promises and keep evaluating if the fastest one was a reject?
const myPromises = [p1, p2, p3]
const winner = Promise.race(myPromises) // this fails if winning promise rejects
p1 wins but returns 500 so promise rejects
p2 comes in second with a 200, return p2
You want to use Promise.any() instead.
This will resolve when the first promise you pass it resolves and will ignore rejected promises unless all of them reject (none resolved).
So, if the first two in your example reject immediately, it will wait for the outcome of the third promise.
It sounds like Promise.any() does exactly what you want.

How to test a promise has a catch method defined?

I have a method that returns a promise.
doStuf() {
return somePromise
.then(...)
.catch(e => {...})
}
I want to write a test that makes sure the promise returned by the doStuff method has a catch method.
How to do that ?
That is not a useful thing to test. Consider that doStuf may be refactored later into something more multi-layered, where several then-catch chains may exist, or the implementation will change in some other ways.
You don't actually want to test the implementation, because you can simply look at it to see whether the promise has a catch. No, you want to test the behaviour of doStuf. You want to assert that doStuf returns a promise, and that that promise returns the expected value given certain input. E.g.:
doStuf('foo').then(r => /* assert r equals expected outcome */)
doStuf('bar').catch(r => /* assert r equals expected error */)

JS Promises returns

I'm developing with the JS promises and I have a doubt, I have readed a lot of answers but I have not yet understood what's the difference between the following code lines. Can you help me?
Thanks in advance
// Case 1
firstPromise()
.then(() => {
return secondPromise();
})
.then()...
// Case 2
firstPromise()
.then(() => {
secondPromise();
})
.then()...
In the first case, it return the second promise, in the second case it just execute. What's the result in the flow?
The first makes the result of the first then depend on what happens with secondPromise, the second does not.
then creates a new promise. That promise will be resolved/rejected based on the return value of the then callback:
If then returns a thenable value (such as another promise), then's promise is resolved to that thenable and either fulfills when that thenable fulfills or rejects when that thenable rejects.
If the then callback returns a non-thenable value, the promise is fulfilled with that value.
(If the term "thenable" isn't familiar, or you're not clear on the distinction between "fulfill" and "resolve," I go into promise terminology in this post on my blog.)
In your first example, the then callback returns the second promise, so its promise is resolved to secondPromise's promise. In your second example, you're not returning anything, so the result of calling the then callback is the value undefined, so then's promise is fulfilled with the value undefined.

How do promise chains start and finish

I'm a little confused about how the sequencing works in various documents I've come across. For instance, I have seen this sort of thing
let p = Promise.resolve();
for (let x in something)
{
/* do some hairy time consuming code */
p = p.then(dosomething(x));
}
return p.then(finalthing()).catch(pretendnobadthing());
What I really don't understand is that if you do something that takes a large amount of time in the main code, wouldn't that mean that one of the promises could complete before the main code actually got round to to setting up the .then.
Can you always use .then/.catch on a promise, and if its already reached completion, it'll carry on from the point it got to, so it conceptually looks like a big chain that will run to completion at some indeterminate point? And if so, doesn't that mean every time you create a promise chain it'll hang about for ever?
Can you always use .then/.catch on a promise, and if its already reached completion, it'll carry on from the point it got to, so it conceptually looks like a big chain that will run to completion at some indeterminate point?
Yes, you can always use .then/.catch on a promise. When you call p.then(), there are three possibilities.
The promise p is still pending (not fulfilled or rejected yet). If that's the case, then the function reference(s) you passed to .then() are registered as listeners for that promise. So, when a future state transition happens on the promise (either going from pending => fulfilled or from pending => rejected), then the appropriate registered listeners will be called.
The promise p is already fulfilled. If that's the case, then calling .then(f1, f2) will schedule f1 to be called on the next tick (after the current piece of Javascript finishes executing) and it will be passed the saved resolved value.
The promise p is already rejected. If that's the case, then calling .then(f1, f2) will schedule f2 to be called on the next tick (after the current piece of Javascript finishes executing) and it will be passed the saved reject reason.
So, it's perfectly safe to call .then() on a promise that is already fulfilled or rejected. The appropriate listener will just be scheduled to run on the next tick.
The same logic applies to .catch() except it is only interested in cases 1 and 3 above.
Here are
And if so, doesn't that mean every time you create a promise chain it'll hang about for ever?
Promises are just objects like any other objects in Javascript. They will hang around only while other code still has some live reference to them. As soon as there is no longer any way to reach that promise object, they will be eligible for garbage collection just like any other objects in Javascript.
So, if you do:
var p = somePromiseReturningFunction();
p.then(f1).then(f2).then(f3);
Then, p will remain around until somePromiseReturningFunction() is done with any references to the promise that it returned (usually, though not always, this occurs when the promise is finally fulfilled or rejected) and when the variable p goes out of scope. If p never goes out of scope (like when it's global or in some other lasting scope), then it will remain forever (just like any other Javascript object).
There are some misconceptions in your question so let me attempt to square those up.
You're using the construct p = p.then(dosomething(x)); which is likely not correct. You need to pass .then() a function reference. So, unless you want doSomething(x) to execute immediately and it also returns another function that is what you want called as then .then() handler (which seems unlikely here), then this is not the right construct. You probably meant to have:
p = p.then(result => dosomething(x));
or in ES5 syntax:
p = p.then(function(result) {
return dosomething(x)
});
You show the same issue in this too:
return p.then(finalthing()).catch(pretendnobadthing());
which should probably be:
return p.then(finalthing).catch(pretendnobadthing);
Remember, when you use f(), that means to execute f immediately. When you just pass f, that passes a function reference which the underlying function/method you are passing it to can then call later at the time of its choosing which is what you want for .then() and .catch() handlers.
What I really don't understand is that if you do something that takes a large amount of time in the main code, wouldn't that mean that one of the promises could complete before the main code actually got round to to setting up the .then.
First off, my original explanation at the beginning of my answer should explain that calling .then() on an already resolved promise is perfectly fine so this isn't an issue at all. It will just schedule action on the next tick of the event loop.
But, that isn't even the case here because Javascript in the browser and node.js is single-threaded so while your long-running code is running, that promise (who's async action was previously started) can't yet get resolved. Though the underlying async operation may be done and an event may be sitting in the Javascript event queue that will trigger a callback that will resolve the promise, that event in the event queue won't get processed until the current piece of Javascript that is executing is done and returns control to the system.
What I really don't understand is that if you do something that takes a large amount of time in the main code, wouldn't that mean that one of the promises could complete before the main code actually got round to to setting up the .then.
Since common implementations of JavaScript never run it in parallel, no. Nevertheless,
Can you always use .then/.catch on a promise, and if its already reached completion, it'll carry on from the point it got to, so it conceptually looks like a big chain that will run to completion at some indeterminate point?
yes! This is a big advantage of promises. Take this for example:
var promise = Promise.resolve(5); // already resolved here
setTimeout(function () {
promise.then(function (x) {
console.log(x); // still logs 5
});
}, 1000);
And if so, doesn't that mean every time you create a promise chain it'll hang about for ever?
Until it’s resolved, yes, but if the promise has already been resolved and there are no ways to reference it anymore, it can be disposed of like any other object.

Why does cancelling a bluebird promise in a callback stop setInterval?

Promise = require 'bluebird'
cb = ->
console.log 'callback!'
p = Promise.resolve(5)
.cancellable()
.tap -> p.cancel()
setInterval(cb, 100)
The cb function only is only called once. Commenting out .tap -> p.cancel() allows it to run repeatedly. Adding a try block doesn't help. Perhaps this is something obvious, but I did some research and can't find an explanation.
It would seem that the act of returning the value of p.cancel() from the tap handler is causing bluebird to go into some sort of infinite loop. You never see the second 'callback!' because the execution context is stuck in this loop before 100 ms have elapsed.
I'm still far from understanding all the factors at play here (see below), but it looks like this can be fixed by not returning p.cancel():
Promise = require 'bluebird'
cb = ->
console.log 'callback!'
p = Promise.resolve(5)
.cancellable()
.tap ->
p.cancel()
null
setInterval(cb, 100)
Edit: Ok, after looking at the source and unknotting my brain a few times, I think it boils down to this:
Execution is getting stuck in an infinite loop here, where .cancel() tries to climb up the promise chain:
while ((parent = promiseToReject._cancellationParent) !== undefined &&
parent.isCancellable()) {
promiseToReject = parent;
}
The salient points are the following:
p.cancel() returns p.
.tap() returns a promise that resolves whenever the promise returned from its handler resolves (if it returns a promise)
p is the promise that .tap() returns
In other words, p is a promise that will resolve after p resolves. It is its own ancestor in the promise chain (at least, I think so).
When .cancel() tries to climb up the promise chain to find a cancellable promise, it happens upon this incestuous relationship and starts going in circles forever.
In the end, it's an unfortunate consequence of CoffeeScript's eagerness to turn almost everything into a return statement. But I would imagine that there's some way Bluebird could detect loops in promise chains and prevent an infinite loop from happening here.
I have filed an issue for this on the bluebird GitHub repository, but as the ensuing discussion reveals, this use of .cancel() doesn't really make any sense at all anyway.

Categories