Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to understand this Promise execution order?

I don’t understand why this piece of code results in such an order? Could anyone elaborate on this? I thought Promises were like a FIFO queue, but the nested Promise functions seems a little bit unpredictable, or maybe using some other data structure?

new Promise(resolve => {
    resolve()
  })
  .then(() => {
    new Promise(resolve => {
        resolve()
      })
      .then(() => {
        console.log(1)
      })
      .then(() => {
        console.log(2)
      })
      .then(() => {
        console.log(3.1)
      })
  })
  .then(() => {
    console.log(1.1)
    new Promise((resolve => {
        resolve()
      }))
      .then(() => {
        new Promise(resolve => {
            resolve()
          })
          .then(() => {
            console.log(4)
          })
          .then(() => {
            console.log(6)
          })
      }).then(() => {
        console.log(5)
      })
  }).then(() => {
    console.log(3)
  })
console.log(0)

Output:

0
1
1.1
2
3
3.1
4
5
6
like image 670
LeoShi Avatar asked Dec 13 '22 10:12

LeoShi


1 Answers

Promises are async. This means everytime you create a new promise- a new async operation starts.

What is async operation in JS? First you need to understand that JS operates on a single thread no matter what you do. So, to make it looks like its asynchronous- there is something called the "event loop" (took the link from comment to original post, tnx @Taki for the great source).

In general, the event loop stores all the async functions and "slips" in the actions between the main code actions. This is really over-simplified explanation, refer to the link to read more, but thats the gist of it.

So basically, there is no "FIFO" queue here- the async functions order is literally depends on stuff like your processor speed, your operating system, etc.

BUT- there is a way to make sure one async action does get performed only after another one finishes, and this is the .then clause. The thing is, it only assures the specific function inside the .then will be performed after the specific promise it was concatenated to, but it does not say anything about the order of it in regars to other async operations (promises) in the event loop. So for example in your code:

new Promise(resolve => {
    resolve() // PROMISE A
  })
  .then(() => {
    new Promise(resolve => {
        resolve() // PROMISE B
      })
      .then(() => {
        console.log(1) //PROMISE C
      })
      .then(() => {
        console.log(2)
      })
      .then(() => {
        console.log(3.1)
      })
  })
  .then(() => {
    console.log(1.1) // PROMISE D
    new Promise((resolve => {
        resolve()
      }))

I took part of it to explain:

so, Promise A resolves first. this assures that promise B will resolve now. here is when things gets complicated: since promise B is resolved, both promise C and D now get into event loop! why? because Promise A had 2 .then clauses, so when the first one ends- event loop takes the 2nd one which is promise D. but the first .then clause had also a .then clause of his own - promise C, which also enters the event loop.

THERE IS NO CONNECTION BETWEEN PROMISE D AND C! They could be performed in any order. keep that logic and you'll see how it works out for the rest of the promises, and also if you try to run it on different OS it might be that promises order will be different because of different implementations of the OS for the event loop.

Hope this helps you to understand a little.

DISCLAIMER: I have not much experience in JS, but promises really intrigued me so I did a deep research about it. I'm standing behind everything I wrote here, but if there are any corrections to my explanation I'd love to hear!

EDIT

The answer beneath me is also correct but with no explanation, so let me add to it: When you do not return anything inside a promise (or a .then clause, which also returns a promise), it will implicitly return a resolved promise with no value before going out of the promise, basically like adding a return new Promise.resolve() after teh console.log in promise C, for example. When its done like this, all the .then clauses coming after promise B will only enter the event loop after the previous one ended (e.g b ends, so C goes into loop, then the next .then and so on), but between them other promises or .then clauses (like promise D) can enter as well.

But, when you RETURN the promise that has the .then clauses chained to it- it makes sure the whole block of the promise + then clauses goes into event loop as one in order, so the .then clauses will also be performed in the order you wanted :)

tnx @Eugene Sunic for the addition!

like image 188
Gibor Avatar answered Dec 20 '22 21:12

Gibor