Detailed explanation of the sequence of setTimeout, setInterval, promise and async/await (in many cases, very detailed ~)

This article is very long and lists a lot of situations.
Before reading this article, if you have enough time, please create a new project to practice with this article.
Each piece of code has a detailed explanation, but the impression will be deeper if you try it yourself~

setInterval: indicates how often it is executed. clearInterval(timer) is required to stop it. Without clearInterval(timer), it will be faster and faster!
setTimeout: indicates how long it will take to execute. It will only be executed once!
For example, this code outputs the current time in the console every second and stops after 5 seconds.
Upper code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test</title>
  </head>
  <body>
    <div id="root"></div>
    <script>
      let timer
      timer = setInterval(function () {
        console.log(new Date())
      }, 1000)
      setTimeout(function () {
        clearInterval(timer)
      }, 5000)
    </script>
  </body>
</html>

Operation results:

What if setTimeout is set to 0?
Modify the following code:

<script>
      let timer
      timer = setInterval(function () {
        console.log(new Date())
      }, 1000)
      setTimeout(function () {
        clearInterval(timer)
      }, 5000)
      setTimeout(function () {
        console.log('hello')
      }, 0)
    </script>

Operation results:

Plus promise

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test</title>
  </head>
  <body>
    <div id="root"></div>
    <script>
      console.log('1')
      setTimeout(() => {
        console.log('2')
      }, 0)
      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
        }, 0)
        console.log('4')
      })

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>
  </body>
</html>

Look at the results:

This paragraph is not called at all.

promise.then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })

Why, because there is no resolve()
What happens after you add resolve()?

<script>
      console.log('1')
      setTimeout(() => {
        console.log('2')
      }, 0)
      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          setTimeout(() => {
            resolve()
          }, 0)
        }, 0)
        console.log('4')
      })

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>

Is it possible to output whenever resolve() occurs?
The answer is: No
Replace "2" with the following:

<script>
      console.log('1')

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          setTimeout(() => {
            resolve()
          }, 0)
        }, 0)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>


It can be seen that the positions of 3 and 2 have indeed changed, but the promise part, that is, 5 and 6, is still the last.

What if there are multiple settimeouts? Let's add a "2.5" to try:

<script>
      console.log('1')

      setTimeout(() => {
        console.log('2.5')
      }, 0)

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          setTimeout(() => {
            resolve()
          }, 0)
        }, 0)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>

The output is:

It can be seen that it is from top to bottom~

What if we put resolve() and console.log('3 ') together?

<script>
      console.log('1')

      setTimeout(() => {
        console.log('2.5')
      }, 0)

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          resolve()
        }, 0)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>

Execution results:

It can be seen that the setTimeout set in the setTimeout will be finally executed.

What if you change the execution time? Change the time of 2.5 output to 0.1s after operation?

<script>
      console.log('1')

      setTimeout(() => {
        console.log('2.5')
      }, 100)

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          setTimeout(() => {
            resolve()
          }, 0)
        }, 0)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>

Execution results:

It will appear at the end!

What if the resolve() execution time is delayed?

<script>
      console.log('1')

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          resolve()
        }, 100)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>

Operation results:

Should we execute first or first~

What happens with async/await? Something magical happened!

<script>
      console.log('1')

      setTimeout(() => {
        console.log('2.5')
      }, 0)

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          resolve()
        }, 0)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      async function async1() {
        console.log('async1 start')
        await async2()
        console.log('async1 end')
      }
      async function async2() {
        console.log('async2')
      }

      console.log('script start')
      async1()
      console.log('script end')

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>

The output is:

Async function returns a Promise object. When the function is executed, once await is encountered, it will return first, wait until the triggered asynchronous operation is completed, and then execute the following statements in the function body. It can be understood as giving up the thread and jumping out of the async function body.

setTimeout will be executed after async/await. However, the promise content will be executed before async/await.

Confusion: what happens when setTimeout is stuffed into async/await?

<script>
      console.log('1')

      setTimeout(() => {
        console.log('2.5')
      }, 0)

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          resolve()
        }, 0)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      async function async1() {
        console.log('async1 start')
        setTimeout(() => {
          console.log('async1 start setTimeout')
        }, 0)
        await async2()
        console.log('async1 end')
        setTimeout(() => {
          console.log('async1 end setTimeout')
        }, 0)
      }
      async function async2() {
        console.log('async2')
        setTimeout(() => {
          console.log('async2 setTimeout')
        }, 0)
        setTimeout(() => {
          console.log('async2 setTimeout after')
        }, 100)
      }

      console.log('script start')
      async1()
      console.log('script end')

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>

Output result: (look carefully)

Therefore, the setTimeout inserted in async/await will be finally executed.

Ultimate chaos:

let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          setTimeout(() => {
            resolve()
          }, 0)
        }, 0)
        console.log('4')
      })

When does resolve() in setTimeout in Promise execute?
Full code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test</title>
  </head>
  <body>
    <div id="root"></div>
    <script>
      console.log('1')

      setTimeout(() => {
        console.log('2.5')
      }, 0)

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('3')
          setTimeout(() => {
            resolve()
          }, 0)
        }, 0)
        console.log('4')
      })

      setTimeout(() => {
        console.log('2')
      }, 0)

      async function async1() {
        console.log('async1 start')
        setTimeout(() => {
          console.log('async1 start setTimeout')
        }, 0)
        await async2()
        console.log('async1 end')
        setTimeout(() => {
          console.log('async1 end setTimeout')
        }, 0)
      }
      async function async2() {
        console.log('async2')
        setTimeout(() => {
          console.log('async2 setTimeout')
        }, 0)
        setTimeout(() => {
          console.log('async2 setTimeout after')
        }, 100)
      }

      console.log('script start')
      async1()
      console.log('script end')

      promise
        .then((res) => {
          console.log('5')
        })
        .then((res) => {
          console.log('6')
        })
      console.log('7')
    </script>
  </body>
</html>

Output results:

Ah, so it is. This part will be finally executed!
(the author is about to fall into chaos...)

Take a break... Go on~
Stack experiment of setTimeout:

<script>
      setTimeout(() => {
        console.log('1')
        setTimeout(() => {
          console.log('1.2.before3')
          setTimeout(() => {
            console.log('1.3.1')
          }, 0)
          setTimeout(() => {
            console.log('1.3.2')
          }, 0)
        }, 0)
        setTimeout(() => {
          console.log('1.2.after3')
        }, 0)
      }, 0)

      setTimeout(() => {
        console.log('2')
        setTimeout(() => {
          console.log('2.2.1')
        }, 0)
        setTimeout(() => {
          console.log('2.2.2')
        }, 0)
      }, 0)

      setTimeout(() => {
        console.log('3')
      }, 0)
    </script>

Output results:

Facts have proved that setTimeout comes layer by layer, and it is important to understand the "batch".

You can try this pyramid like structure by yourself. First build the first layer of foundation, and then build this idea upward.

So far, you have read all the contents. Thank you for watching. Practice is more important than understanding. Readers are advised to create a new project and try all kinds of situations by yourself. There are more nesting situations that are not listed in this paper. If there are diligent students, please discuss them~

Supplement:

setTimeout is not the reason for punctuality

  • Because JavaScript is an interpreter of a single line program, it can only execute one piece of code in a certain time.
  • In order to control the code to be executed, there is a JavaScript task queue.
  • These tasks are executed in the order they are added to the queue.
  • The second parameter of setTimeout() tells JavaScript how long it will take to add the current task to the queue. If the queue is empty, the added code will be executed immediately; If the queue is not empty, it will be executed after the previous code is executed

Keywords: Javascript node.js Front-end Web Development

Added by palpie on Sat, 04 Sep 2021 21:42:54 +0300