With regard to these great two sources: NZakas - Returning Promises in Promise Chains and MDN Promises, I would like to ask the following:
Each time that we return a value from a promise fulfillment handler, how is that value passed to the new promise returned from that same handler?
For instance,
let p1 = new Promise(function(resolve, reject) {
resolve(42);
});
let p2 = new Promise(function(resolve, reject) {
resolve(43);
});
let p3 = p1.then(function(value) {
// first fulfillment handler
console.log(value); // 42
return p2;
});
p3.then(function(value) {
// second fulfillment handler
console.log(value); // 43
});
In this example, p2
is a promise. p3
is also a promise originating from p1
's fulfillment handler. However p2 !== p3
. Instead p2
somehow magically resolves to 43
(how?) and that value is then passed to p3
's fulfillment handler. Even the sentence here is confusing.
Could you please explain to me what exactly is going on here? I am totally confused over this concept.
Let’s say that throwing inside
then()
callback rejects the result promise with a failure, and returning fromthen()
callback fulfills the result promise with a success value.But sometimes, even inside the continuation, we don’t know whether we have succeeded or not. We need more time.
However, if I do async work there, it would be too late to
return
orthrow
, wouldn’t it?This is why Promises wouldn’t be useful if you couldn’t resolve to another Promise.
It doesn’t mean that in your example
p2
would becomep3
. They are separate Promise objects. However, by returningp2
fromthen()
that producesp3
you are saying “I wantp3
to resolve to whateverp2
resolves, whether it succeeds or fails”.As for how this happens, it’s implementation-specific. Internally you can think of
then()
as creating a new Promise. The implementation will be able to fulfill or reject it whenever it likes. Normally, it will automatically fulfill or reject it when you return:Again, this is very much pseudo-code but shows the idea behind how
then()
might be implemented in Promise implementations.If we want to add support for resolving to a Promise, we just need to modify the code to have a special branch if the
callback
you pass tothen()
returned a Promise:Let me clarify again that this is not an actual Promise implementation and has big holes and incompatibilities. However it should give you an intuitive idea of how Promise libraries implement resolving to a Promise. After you are comfortable with the idea, I would recommend you to take a look at how actual Promise implementations handle this.
a simplified version how this works (only pseudocode)
the whole thing is quite more complicated since you have to take care, wether the promise has already been resolved and a few more things.
I'll try to answer the question "why
then
callbacks can returnPromise
s themselves" more canonical. To take a different angle, I comparePromise
s with a less complex and confusing container type -Array
s.A
Promise
is a container for a future value. AnArray
is a container for an arbitrary number of values.We can't apply normal functions to container types:
We need a mechanism to lift them into the context of a specific container:
But what happens when the provided function itself returns a container of the same type?
sqra
acts like expected. It just returns a nested container with the correct values. This is obviously not very useful though.But how can the result of
sqrp
be interpreted? If we follow our own logic, it had to be something likePromise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}}
- but it is not. So what magic is going on here?To reconstruct the mechanism we merely need to adapt our
map
method a bit:flatten
just takes a function and a value, applies the function to the value and unwraps the result, thus it reduces a nested array structure by one level.Simply put,
then
in the context ofPromise
s is equivalent tomap
combined withflatten
in the context ofArray
s. This behavior is extremely important. We can apply not only normal functions to aPromise
but also functions that itself return aPromise
.In fact this is the domain of functional programming. A
Promise
is a specific implementation of a monad,then
isbind
/chain
and a function that returns aPromise
is a monadic function. When you understand thePromise
API you basically understand all monads.Basically
p3
isreturn
-ing an another promise :p2
. Which means the result ofp2
will be passed as a parameter to the nextthen
callback, in this case it resolves to43
.Whenever you are using the keyword
return
you are passing the result as a parameter to nextthen
's callback.Your code :
Is equal to:
Btw, I've noticed that you are using ES6 syntax, you can have a lighter syntax by using fat arrow syntax :