why resolve function runs once in thenable

94 Views Asked by At

Helo, I'm trying to understand promises and don't understand why this code:

class Thenable {
  constructor(num) {
    this.num = num;
  }
  then(resolve, reject) {
    console.log(resolve); // function() { native code }
    // resolve with this.num*2 after the 3 second
    new Promise( (resolve, reject) => {
       setTimeout(() => resolve(this.num * 2), 3000); // (**)
    }).then((ret)=>{ resolve('wow'); resolve('inside '+ret)})
  }
}

new Promise(resolve => resolve(1))
  .then(result => {
    return new Thenable(result); // (*)
  })
  .then((r) => { console.log(r) } ); 
console.log('done')

prints only 'wow' and don't print 'inside 2' :

done 
ƒ () {}
wow 

Thenable.then is an ordinary function, when I call it with .then((r) => { console.log(r) } ) the resolve became function with one argument who calls console.log . Why it calls only first resolve('wow') and doesn't call next resolve('inside '+ret) ? Does js engine somehow rewrite my .then function and left only one resolve invocation?

1

There are 1 best solutions below

0
Jeff Bowman On

Promises can only settle (resolve or reject) once. When you pass back a thenable, the resolution function converts the Thenable result to a native Promise, which only resolves once even if your thenable calls the resolution function multiple times.


From your question:

the resolve became function with one argument who calls console.log

That's not true. resolve is the function that resolves the internal native Promise created by the then call above your line (*), as in the MDN docs for the Promise constructor:

If [the resolution function]'s called with a thenable value (including another Promise instance), then the thenable's then method is saved and called in the future (it's always called asynchronously). The then method will be called with two callbacks, which are two new functions with the exact same behaviors as the resolveFunc and rejectFunc passed to the executor function. If calling the then method throws, then the current promise is rejected with the thrown error.

Even if your Thenable implementation can resolve multiple times (against spec), the native Promise created by then and Promise.resolve can only resolve once, and that Promise is the one that your console.log handler is chained onto. From the same Promise constructor docs above:

Calling resolveFunc causes the promise to become resolved, so that calling resolveFunc or rejectFunc again has no effect.