Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between resolve(thenable) and resolve('non-thenable-object')?

I was trying to understand the difference between resolve(thenable) and resolve('non-thenable-object').

In examples below, use promise instead of thenable because promise is also thenable and may be easier to understand.

Demo1: resolve(promise)

let resolvePromise = new Promise(resolve => {
  let resolvedPromise = Promise.resolve()
  resolve(resolvedPromise)
})
resolvePromise.then(() => {
  console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
  console.log('promise1')
})
resolvedPromiseThen
  .then(() => {
    console.log('promise2')
  })
  .then(() => {
    console.log('promise3')
  })

result:

  • promise1
  • promise2
  • resolvePromise resolved
  • promise3

Demo2: resolve('non-thenable-object')

let resolvePromise = new Promise(resolve => {
  resolve('non-thenable-object')
})
resolvePromise.then(() => {
  console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
  console.log('promise1')
})
resolvedPromiseThen
  .then(() => {
    console.log('promise2')
  })
  .then(() => {
    console.log('promise3')
  })

result:

  • resolvePromise resolved
  • promise1
  • promise2
  • promise3

So, I checked the spec and found Promise Resolve Functions . Then got to PromiseResolveThenableJob and EnqueueJob.

So, according to the spec, I think demo1 was like

Demo3:

let resolvePromise = new Promise(resolve => {
  let resolvedPromise = Promise.resolve()
 // resolve(resolvedPromise)
  // works like 
  Promise.resolve().then(() => {
    Promise.resolve(resolvedPromise).then(() => {
      resolve()
   })
  })
})
resolvePromise.then(() => {
  console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
  console.log('promise1')
})
resolvedPromiseThen
  .then(() => {
    console.log('promise2')
  })
  .then(() => {
    console.log('promise3')
  })

I think so because Promise Resolve Functions says:

  1. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, « promise, resolution, thenAction »).

And PromiseResolveThenableJob says:

This Job uses the supplied thenable and its then method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.

Also, I think demo2 works like

Demo4:

//let resolvePromise = new Promise(resolve => {
  //resolve('str')
//})
//works like
let resolvePromise = Promise.resolve('str')

resolvePromise.then(() => {
  console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
  console.log('promise1')
})

resolvedPromiseThen
  .then(() => {
    console.log('promise2')
  })
  .then(() => {
    console.log('promise3')
  })

As the Promise Resolve Functions says:

If IsCallable(thenAction) is false, then Return FulfillPromise(promise, resolution).

Though the results between Demo1-Demo3 and Demo2-Demo4 are equal, I am still not sure if I was right. So, I am here to ask

whether my logic is right? If not, how do you explain the different orders between resolve(thenable) and resolve(non-thenable) ?

like image 403
xianshenglu Avatar asked Dec 22 '18 08:12

xianshenglu


2 Answers

Yes, your logic looks right.

new Promise(resolve => resolve('non-thenable-object')) is equivalent to Promise.resolve('non-thenable-object') for all purposes.

In your Demo3 I would however recommend to leave out Promise.resolve(resolvedPromise). I'm not sure whether that was intentional or not, but Promise.resolve does have a shortcut when its argument is already a promise, and then returns the resolvedPromise as-is. You'd rather write

new Promise((resolve, reject) => {
  let resolvedPromise = Promise.resolve();
  // resolve(resolvedPromise) works like 
  Promise.resolve().then(() => resolvedPromise.then(resolve, reject));
});
like image 72
Bergi Avatar answered Nov 06 '22 11:11

Bergi


After reading the specification and testing many times I thought I might get it.

Before we start, we have to settle something.

let's call it RESOLVE() when using resolve in Promise executor. For example, RESOLVE(thenable) means the code like:

  new Promise((resolve,reject)=>{
    resolve(thenable)
  })

while resolve(thenable) means Promise.resolve(thenable)

Ok, let's begin.

Promise.resolve('non-thenable') and RESOLVE('non-thenable')

When we are using Promise.resolve('non-thenable') it comes to Promise.resolve

enter image description here

Then it comes to PromiseResolve

enter image description here

That's the where Promise.resolve('non-thenable') was transformed to

new Promise(resolve=>{
  resolve('non-thenable')
})

So, we have the conclusion:

Promise.resolve('non-thenable') can be transformed into RESOLVE('non-thenable')


RESOLVE(thenable)

demo1

let resolveThenable = new Promise((resolve, reject) => {
  let thenable = {
    then: function (resolve, reject) {
      console.log('in thenable')
      resolve(42)
    }
  }
  resolve(thenable)
  // works like
  // Promise.resolve().then(() => {
  //   thenable.then(resolve)
  // })
  // should be ?
  // Promise.resolve().then(() => {
  //   thenable.then.[[Value]](resolve)
  // })
  // equivalent to?
  // Promise.resolve().then(() => {
  //   thenable.then(resolve)
  // })
})
resolveThenable.then(() => {
  console.log('resolveThenable resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
  console.log('promise1')
})
resolvedPromiseThen
  .then(() => {
    console.log('promise2')
  })
  .then(() => {
    console.log('promise3')
  })

// 'in thenable'
// 'promise1'
// 'resolveThenable resolved'
// 'promise2'
// 'promise3'

According to Promise Resolve Functions, when we were using RESOLVE(thenable) it comes to

enter image description here

Then it comes to PromiseResolveThenableJob

enter image description here

This would make RESOLVE(thenable) works like

  Promise.resolve().then(() => {
    thenable.then.[[Value]](resolve)
  })

So, I thought it is equivalent to

  Promise.resolve().then(() => {
    thenable.then(resolve)
  })

Which got the same result as RESOLVE(thenable).

So, we can say RESOLVE(thenable) can be transformed to

  Promise.resolve().then(() => {
    thenable.then(resolve)
  })
demo2

let resolvePromise = new Promise((resolve, reject) => {
  let resolvedPromise = Promise.resolve()
  resolve(resolvedPromise)
  // works like
  // Promise.resolve().then(() => {
  //   resolvedPromise.then(() => {
  //     resolve()
  //   })
  // })
  // should be?
  // Promise.resolve().then(() => {
  //   resolvedPromise.then.[[Value]](resolve,reject)
  // })
  // equivalent to ?
  // Promise.resolve().then(() => {
  //   resolvedPromise.then(resolve)
  // })
  // equivalent to ?
  // Promise.resolve().then(() => {
  //   resolvedPromise.then(() => {
  //     resolve()
  //   })
  // })
})
resolvePromise.then(() => {
  console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
  console.log('promise1')
})
resolvedPromiseThen
  .then(() => {
    console.log('promise2')
  })
  .then(() => {
    console.log('promise3')
  })

// 'promise1'
// 'promise2'
// 'resolvePromise resolved'
// 'promise3'

When we talked about RESOLVE(resolvedPromise), we can find the spec doesn't distinguish thenable from promise. So, in the same way, RESOLVE(resolvedPromise) can be transformed into

  Promise.resolve().then(() => {
    resolvedPromise.then(resolve)
  })

Though, in this case, the order between RESOLVE(thenable) and RESOLVE(promise) is different. Because thenable.then is a sync operation while resolvedPromise.then is an async operation. They are not the same then method.

So, here is our conclusion:

Both RESOLVE(thenable) and RESOLVE(promise) can be transformed into

 new Promise((resolve, reject) => {
      Promise.resolve().then(() => {
        thenable.then(resolve)
      })
 })

Promise.resolve(thenable)

It is quite simple when using Promise.resolve(promise) because it returns promise argument.

However, things become complicated when using Promise.resolve(thenable) and the thenable is not a promise. Let's call it Promise.resolve(nonPromiseThenable).

According to Promise.resolve ( x )

enter image description here

Then it comes to

enter image description here

So, Promise.resolve(nonPromiseThenable) can be transformed to

 new Promise(resolve => { 
   resolve(nonPromiseThenable)
 })

And finally comes to

 new Promise(resolve => { 
   Promise.resolve().then(() => { 
     nonPromiseThenable.then(resolve) 
   }) 
 })

You can test it in the demo below.

var thenable = {
  then(resolve, reject) {
    resolve(1)
  }
}
// code transformation 
Promise.resolve(thenable).then(res => {
  console.log(res)
})
// equal 
// new Promise(resolve => { 
//   resolve(thenable) 
// }).then(res => { 
//   console.log(res) 
// }) 
// equal 
// new Promise(resolve => { 
//   Promise.resolve().then(() => { 
//     thenable.then(resolve) 
//   }) 
// }).then(res => { 
//   console.log(res) 
// }) 

new Promise(resolve => resolve(2))
  .then(res => {
    console.log(res)
  })
  .then(res => console.log(3))

In the end, let's make a conclusion:

  • Promise.resolve('nonThenable') can be transformed into RESOLVE('nonThenable'). They have the same effects.
  • Promise.resolve(thenable) is different from RESOLVE(thenable). They have different effects.
  • RESOLVE(thenable) and RESOLVE(promise) can be transformed into new Promise((resolve, reject) => { Promise.resolve().then(() => { thenable.then(resolve) }) })
  • Promise.resolve(promise) === promise while Promise.resolve(nonPromiseThenable) can be transformed into new Promise(resolve => { Promise.resolve().then(() => { nonPromiseThenable.then(resolve) }) })
like image 43
xianshenglu Avatar answered Nov 06 '22 11:11

xianshenglu