Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
156 views
in Technique[技术] by (71.8m points)

javascript - Resolve order of Promises within Promises

For the below code

function inner () {
  new Promise(function(resolve,reject){
    resolve()
  }).then(function(){
    console.log('Inner Promise')
  })
}
function outer() {
  return new Promise(function(resolve, reject){
    resolve()
    inner()
  })
}

outer().then(function(data) {
  console.log('Outer Promise')
})

The output is

Inner Promise
Outer Promise

I thought the outer resolve would be the first to enter the JS Message Queue followed by the inner resolve. However the JS Event Loop fires the Inner resolve first then followed by Outer resolve.

What does Promise resolve do internally?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In a nutshell, you get the behavior you see because the .then() method on the inner() promise runs first before the .then() method on the outer() promise and thus it's handler gets queued first (see step by step explanation below for why this is).

What does Promise resolve do internally?

resolve() changes the internal state of the promise to Fulfilled. At that moment, if there are any .then() handlers already attached to the promise, they are added to a queue to be executed when the stack unwinds and the current running path of Javascript finishes and returns control back to the system. Note, as you will see in this case (when you read the step-by-step analysis below), if there are not yet any .then() handlers that have been registered, nothing can yet be added to the queue.

I thought the outer resolve would be the first to enter the JS Message Queue followed by the inner resolve. However the JS Event Loop fires the Inner resolve first then followed by Outer resolve.

Promise resolve actions are not added to the queue. resolve() is synchronous. It changes the state of the current promise to the Fulfilled state immediately. If, at the time the promise is resolved, there are any .then() handlers already register, then they are what is added to a queue. But, in both your promises, at the moment each of your promises are resolved, there are no .then() handlers yet attached. So, those .then() handlers won't be queued at the point the promise is resolved. Instead, they will be queued later when the .then() method actually runs and registers them.

Here's a bit of an analysis of how your code runs and a likely explanation:

  1. First you call outer(). This creates a Promise object and synchronously calls the promise executor callback you pass it.
  2. That callback calls resolve() which will queue up the calling of any currently attached .then() handlers. Note, that at the moment you call resolve(), there are no .then() handlers yet because in this code outer().then(), you're still running outer() and the .then() after it has not yet run so there isn't actually yet anything to queue up yet (this is probably key to the ordering you observe - read on for further details).
  3. Then, the code calls inner(). That creates a new promise and then (still running synchronously) calls the promise executor callback you pass there which calls resolve(). Again, there are not yet any .then() handlers attached so there is still yet nothing else to schedule for future execution.
  4. Now, the Promise executor inside of inner() returns and the .then() method is called on that promise inside of inner(). This promise has already been resolved so, when this .then() handler is called, the promise knows to schedule it to run in the future. Since all .then() handlers are called asynchronously when the stack has unwound to only platform code, it is not run immediately, but it is scheduled to run in the future by puttiing it in a queue. It is implementation dependent exactly how this queue works (macro task or micro task, etc...), but we know it is guaranteed by the Promise specification to run after the current synchronous piece of JS code that is executing finishes running and returns control back to the system.
  5. Now inner() returns (code is still running synchronously).
  6. Now outer() returns and the .then() method in outer().then() runs. Just like in the previous example, when this .then() method is called, the host promise is already resolved. So, the promise engine will schedule the .then() handler callback to be run by adding it to the queue.
  7. If these two .then() handlers in steps 4 and 6 are queued in the order they were run (which would be the logical implementation), then you would see the .then() handler on inner() run first and then the .then() handler on outer() would run since inner().then() ran first beforeouter().then()`. That is what you observe.
  8. Even though outer() is resolved before inner() is, at the time outer() is resolved, there are not .then() handlers attached so there is nothing to schedule for future execution when it is resolved. This is likely why even though it is resolved first, its .then() handlers don't run first. Once both inner() and outer() are resolved, it is inner's .then() method that runs first, so it gets first crack at scheduling a .then() handler to run and this is what you observe.

You can get some additional context for what's going on by reading and studying these references:

What is the order of execution in javascript promises

Difference between microtask and macrotask within an event loop context.


If you wanted to more explicitly specify that the inner .then() handler would fire first, you can simply chain it to the outer() promise like this:

function inner () {
  return new Promise(function(resolve,reject){
    resolve();
  }).then(function(){
    console.log('Inner Promise')
  })
}
function outer() {
    // Add return here to chain the inner promise
    // make to make sure that outer() does not resolve until
    // inner() is completely done
    return inner();
}

outer().then(function(data) {
  console.log('Outer Promise')
})

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...