Ilya Loskutov
Ilya Loskutov

Reputation: 2201

Possible contradiction between Promises/A+ spec and ECMAScript promises?

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?

Upvotes: 1

Views: 75

Answers (2)

Bergi
Bergi

Reputation: 664630

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.

Upvotes: 1

trincot
trincot

Reputation: 350345

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.

Upvotes: 0

Related Questions