promises-aplus / promises-spec

An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
https://promisesaplus.com/
Creative Commons Zero v1.0 Universal
1.84k stars 171 forks source link

Question about **promise resolution procedure** 2.3.2 #269

Open nilptr opened 6 years ago

nilptr commented 6 years ago
const p1 = new Promise((resolve, reject) => {
    const p2 = new Promise((resolve1) => {
        setTimeout(() => {
            resolve1(1);
        }, 1000);
    });

    resolve(p2);

    reject(2);
});

p1.then((value) => {
    console.log('fulfilled:', value);
}, (reason) => {
    console.log('rejected:', reason);
});

// my implementation which has pass all test cases of <https://github.com/promises-aplus/promises-tests>.
// reject: 2

// Node.js & Safari & Firefox & bluebird
// fulfilled: 1

The specification says:

2.1.1 When pending, a promise:    2.1.1.1 may transition to either the fulfilled or rejected state.

2.3.2 If x is a promise, adopt its state [3.4]:    2.3.2.1 If x is pending, promise must remain pending until x is fulfilled or rejected.    2.3.2.2 If/when x is fulfilled, fulfill promise with the same value.    2.3.2.3 If/when x is rejected, reject promise with the same reason.

According to my understanding of these, when resolve p1 with p2, p1 will remain pending until p2 is fulfilled 1 second later, so p1 can be rejected with 2 immediately. However in Node.js / browser / bluebird these implementations, p1 adopts the state of p2. Which behavior is correct ?

ForbesLindesay commented 6 years ago

"adopt its state" means "permanently adopt its state" i.e. once you've adopted the state of p2, you ignore anything that would modify your own state, as you have already adopted the state of p2.

This is probably tied down more tightly in the ECMA Script specification, because that actually specifies how resolve and reject behave, but this spec only actually specifies what happens to values returned from .then callbacks.

ForbesLindesay commented 6 years ago

A test case like:

Promise.resolve(null).then(() => ({
  then(resolve, reject) {
    const p2 = new Promise((resolve1) => {
      setTimeout(() => {
        resolve1(1);
      }, 1000);
    });

    resolve(p2);

    reject(2);
  }
})).then((value) => {
  console.log('fulfilled:', value);
}, (reason) => {
  console.log('rejected:', reason);
});

would actually be covered by the Promises/A+ specification. Your implementation must print "fulfilled: 1" to be Promises/A+