最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin0浏览0评论

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')
  })

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 pleted.

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) ?

Share Improve this question edited Dec 27, 2018 at 11:20 xianshenglu asked Dec 22, 2018 at 8:09 xianshengluxianshenglu 5,3693 gold badges21 silver badges38 bronze badges 3
  • 2 I remend in general to never rely on callback invocation order between unrelated promises. Although the spec gives a specific queuing semantic, it should never be relevant in practice. If you need a specific order because your callbacks depend on each other, be explicit about that by chaining your promises after each other. – Bergi Commented Dec 22, 2018 at 11:47
  • 3 @Bergi, Actually, in practice, I always use promises chain to control the async order. But I really want to understand the spec and principle. – xianshenglu Commented Dec 22, 2018 at 12:26
  • 2 Upvoted for the ment. Nothing is wrong with genuine interest in understanding the specification for the sake of understanding, even if it's not good to rely on in practice. – Patrick Roberts Commented Dec 23, 2018 at 2:13
Add a ment  | 

2 Answers 2

Reset to default 3

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 es to Promise.resolve

Then it es to PromiseResolve

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 es to

Then it es to PromiseResolveThenableJob

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 bee plicated when using Promise.resolve(thenable) and the thenable is not a promise. Let's call it Promise.resolve(nonPromiseThenable).

According to Promise.resolve ( x )

Then it es to

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

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

And finally es 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) }) })

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 remend 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));
});
发布评论

评论列表(0)

  1. 暂无评论