The documentation for promises states that .catch()returns a promise. Does this mean that if you return a promise in your code it will get wrapped in another promise from the .catch?
i.e.
const x = Promise.reject().catch(() => Promise.resolve("test"));
Will the above promise that resolves with the value of "test" be wrapped in another promise from the .catch? To my understanding this is what happens in async functions; they wrap whatever the result is in side of a promise "under the hood". Which could then result in double promises, is that also happening here?
If the promise in the variable x above was put into an existing array and passed to Promise.all(), what would constitute as being resolved? Would the catch block and the inner function have to complete before Promise.all resolves? If so, why is this the case? Why would Promise.all not resolve as soon as the first Promise.reject() is executed? How would it know to wait?
Yes and no ... there are multiple promises involved, but they are chained, not wrapped in each other, or in the final promise "takes on" (the actual phrase is "adopts") the value of the Promise.resolve("test") in your case, so, what you get is a single promise that settles (in this case, resolves) to the value "test"
The 5 Answers are:
yes, it is resolved,
yes, because x is the final promise returned by that expression
because x is the final promise returned by that expression, none of the other Promises are "visible" to the Promise.all,
because x is the final promise returned by that expression, the other Promises are not "visible" to x
that's how promise chains work, there's no waiting, it's just promise chaining at work
You may find The Promise Resolution Procedure - 2.3.2 helps in understanding the inner workings of Promises - 2.3.2 specifically deals with returning a Promise inside .then ... the rest of that resolution procedure is also illuminating.
Related
It is asserted the ECMAScript promises is a Promises/A+ implementation, so they have no contradictions. However, I encountered a behaviour of ecma promises which allegedly is out of line with the Promises/A+.
When we call promise1.then(onFulfilled, onRejected) to listen to the promise1's output, we get as a return value another promise (promise2). When the needed callback (onFulfilled/onRejected) was executed and it, in turn, returned some value x, the spec prescribes to resolve it with the defined [[Resolve(promise2, x)]] function. Let's suppose x happened to be a promise itself (x === promise3), then the steps must be taken is the following:
If x is a promise, adopt its state:
If x is pending, promise2 must remain pending until x is fulfilled or rejected.
If/when x is fulfilled, fulfill promise2 with the same value.
If/when x is rejected, reject promise2 with the same reason.
I wonder what if x is finally fulfilled with yet another promise (promise4) (there are not anything in the way of it, are there?). It can be concluded from the spec excerpt that promise2 must be fulfilled with promise4 too. But it is seemingly not so in the ECMAScript world:
let promise4 = new Promise((resolve) => { resolve(4) })
let promise3 = new Promise((resolve) => {
resolve(promise4);
});
let promise1 = new Promise((resolve) => {
resolve(1);
});
let promise2 = promise1.then((val) => { return promise3 });
promise2.then(val => console.log(val)); // output: 4
In the other words, promise2 is fulfilled with the promise4's value. This behaviour is like one that is defined in the spec for other thenable objects. So don't ECMAScript promises carry out expected type checking and just check whether x has then method?
Let's suppose x happened to be a promise itself, then the steps must be taken is the following: […]
No, they don't need to be taken - they only may be taken if x is a "promise". These steps are an optional ("allowed", not "required") optimisation:
Note 4:
Generally, it will only be known that x is a true promise if it comes from the current implementation. This clause allows the use of implementation-specific means to adopt the state of known-conformant promises.
ECMAScript does not treat its own Promises as "known to be conformant", ignoring these steps. They simply treat native promises like all other thenables. Given there is no way to create an ECMAScript Promise that is fulfilled with another promise, this is equivalent to directly adopting the state.
It can be concluded from the spec excerpt that promise2 must be fulfilled with promise4 too.
No, this does not follow from the Promises/A+ specification. The rule you quote from it
If/when x is fulfilled, fulfill promise2 with the same value.
...is recursive in nature. It should be understood in more elaborate terms as follows:
If/when x is fulfilled, fulfill promise2 with the same value that x fulfilled with.
Now for the part "that x fulfilled with", the same Resolution Procedure applies(!): Indeed, as x resolves with yet another thenable (promise4), it in turn gets locked-in with the next promise in the chain (promise4 in your example). This is not yet the fulfilled value. -- there is an important difference between resolving and fulfilling. This second execution of the Resolution Procedure will make sure that the value that x fulfils with, is the value that promise4 fulfils with.
The chain of locked-in promises can have any length, but the principle remains the same: each will resolve by locking into the next promise through this Promises/A+ Resolution Procedure. When the last one in this chain fulfils (with a non-thenable value), then all promises that are locked-in will get fulfilled with this value.
I'd like to check my understanding of Promise.prototype.then is correct.
In the specs (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) it says:
If a handler function:
returns a value, the promise returned by then gets resolved with the
returned value as its value.
Is it not a bit strange for .then to return a promise object if I just returned something simple like 5 from the .then's callback? How does this promise object resolve? It doesn't seem like it's fetching something from anywhere. Do promises like this always get fulfilled?
function fetchDog(){
fetch("https://dog.ceo/api/breeds/image/random")
.then(response => 5)
.then(data => console.log(data))
};
fetchDog();
A call to then() must return a promise according to the Promises/A+ specification, 2.2.7 (which is also reflected in the EcmaScript specs).
At the time that then() is called on a promise A, a promise B is returned, but the then-callback is not executed at that moment. It is not known yet whether and how that promise B will settle. The then-callback will be called asynchronously, only after the base promise A, has resolved.
When A resolves, the then-callback will be called, and that call will determine how promise B will resolve. So when the callback returns 5, then that will be the value with which promise B resolves. If it is undefined, then that will be it. Only when the returned value is yet another promise C (or at least a thenable), there will be a cascade effect, and promise B will link its resolution to that of C.
The callback function passed to then can't run until the promise it is associated with is resolved, but then has to return immediately, so then returns a promise.
Having the following pice of code...
const array = [
Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)
];
Array.prototype.then = function () {
console.log('Why does this gets triggered?');
}
Promise.all(array)
.then(result => console.log(result))
Why does Promise.all() by itself calls my .then() proto function on the Array?
Of course it must call .then() for each of the elements in the array. That’s obvious. But what is the purpose of doing it over the Array itself?
This behavior is happening on V8
To consider: If you change Promise.all() to Promise.race() this does not happen.
I'm not saying this is a mistake. I just want to understand the reason. If you can quote the EcmaScript specification on the answer I would really appreciate.
Update:
I know Promise.all() returns an array but wrapped on a promise. That is obvious too. If you remove the .then() like...
Promise.all(array)
it still executes the .then()method.
When a resolve() is called, and the value passed to it has a .then property that refers to a function, the normal Promise mechanism will invoke that function. In this case, internal to Promise.all() an array of resolution values is built as each Promise in the source array is resolved. When that finishes, the innards of Promise.all() will call its own resolve(), passing in the array of resolved values.
Well that array will also have a .then() value, inherited from the Array prototype. Thus that resolve() call will invoke the .then() method just like any other Promise resolution would.
Promise resolve() in the spec
The docs for Promise.all() describe the argument as an array of Promises. Is behavior defined if some (or all) of the elements in the array are non-Promise values? For example, in Node 6.10.2:
Promise.all([1, 2, 3]).then(
res => console.log(res)
);
Prints [ 1, 2, 3 ], as expected. Is this behavior (where Promise.all resolves with the same values that it was called with) guaranteed in Node?
Promise.all() is specified in the ES6 specification.
It appears that section 24.4.4.1.1 in that ES6 specification at step 6.i describes how each item in the iterable passed to Promise.all() is passed to a promise constructor (essentially calling Promise.resolve(item)) so that any non-promise gets wrapped in a promise that will resolve itself on the next tick.
You need to follow the overall context of how these steps work to fully understand, but the specific 6.i step is this:
Let nextPromise be Invoke(constructor, "resolve", «nextValue»).
Where constructor (in this context) is Promise and Invoke() is calling the resolve method on it - so it's essentially doing:
let nextPromise = Promise.resolve(nextValue);
Which is how each item in the iterable is wrapped in a promise if it wasn't already a promise.
Then, later in step 6.r, it does this:
Let result be Invoke(nextPromise, "then", «resolveElement, resultCapability.[[Reject]]»).
Which is where it calls .then() on nextPromise which will now work fine even if the item in the iterable was not originally a promise because it has been wrapped in a new promise.
And, in 24.4.4.5 the description of Promise.resolve(x) is as follows:
The resolve function returns either a new promise resolved with the passed argument, or the argument itself if the argument is a promise produced by this constructor.
So, you can see that the value is wrapped in a new promise if it is not already a promise.
Is this behavior (where Promise.all() resolves with the same values that it was called with) guaranteed in Node?
Yes, if the values are not promises, then it will resolve with an array of the same values. It will be a different array, but the same values in the new array. If any of the values are promises, then obviously, the final array contains the resolved value of that promise. This is guaranteed by the specification.
This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 8 years ago.
What is the correct pattern, when coding with promises, to access data coming from long before in a chain of promises?
For example:
do_A.then(do_B).then(do_C).then(do_D).then(do_E_WithTheDataComingFrom_A_And_C_OnlyWhen_D_IsSuccesfullyCompleted)
My current solution: passing along a single JSON structure through the chain, and let each step populate it.
Any opinion about that?
I don't think there's one "correct" pattern for this. Your solution sounds neat, however, it's a bit tightly coupled. It may work great for your situation, but I see a few problems with it as a general pattern:
Participating steps need to agree on the structure of the collector object.
Every step needs to participate in at least the forwarding of the object, which can get tedious if the chain is long and the need for previous data occurs sporadically. This is also inflexible to insertion of steps not written by you (chaining doesn't always happen linearly).
Said differently: Unless do_A|B|C|D and do_E are under your control, you'll need to wrap them with boilerplate to store off your collector object in a closure and convert to and from the functions' natural inputs and results, since the functions wont be "in on" your pattern.
On the other hand, if the functions are in on it, then the data-dependencies between the steps have effectively become hidden inside the functions. This may look clean, but it could become a maintenance problem. For example: if this is a team project, then someone might think they can re-order your steps, absent any no clue in the call-pattern what inputs do_E requires.
I would suggest a more straightforward approach using closures:
var a, c;
do_A()
.then(function(result) { a = result; return do_B(); })
.then(do_C)
.then(function(result) { c = result; return do_D(); })
.then(function() {
return do_E_WithTheDataComingFrom_A_And_C_OnlyWhen_D_Succeeds(a, c);
})
.catch(failed);
There's no collector object to define; do_A|B|C|D and do_E can be generic functions without knowledge of any pattern; there's no boilerplate unless returned data is relied on (do_B and do_D); and the data-dependencies (a and c) are explicit.
Yes, this is the correct way to chain state with actions.
Chaining .then statements is very common and is usually our building block when piping things around. It's at the very core of promises.
What you're doing is both correct and idiomatic.
For the curious spirit let's show this.
In order to verify this - we can check the promises specification.
We want to verify that:
It chains
In the case of a rejection, it doesn't call the handler in the chained then
It rejects the next promise returned from then with the same reason
It executes in sequence passing return value.
Let's verify these in order, using the specification - in particular .then:
1. It chains
7.1 then must return a promise [3.3].
Great, let's verify that it also chains on fullfillment
If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure >[[Resolve]](promise2, x).
Great, so we know that when our promise resolves or rejects then our then handler is called with the appropriate parameter. So .then(do_A).then(do_B) will always work assuming do_A resolves.
2. In the case of a rejection, it doesn't call the handler in the chained then
7.iv. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason.
Great, so it rejects and calls onRejected if it's there, if it doesn't it chains.
3. It rejects the next promise returned from then with the same reason
We just covered that in 2.
4. It executes in sequence passing return value.
That is again
If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
So, if you set onFulfilled it'll run the resolution process. The resolution process itself dictates:
The promise resolution procedure is an abstract operation taking as input a promise and a value, which we denote as [[Resolve]](promise, x). If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.
If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
Where y is the return value of x.
Great! so it works.