本文很长,列举的情况很多。
在阅读本文之前,如果您有充足的时间,请新建一个项目与本文一同实践。
每段代码都有对应的解释,但是自己动手尝试印象才会更深哦~

setInterval:表示多久执行一次,需要clearInterval(timer)来让它停下。要是不clearInterval(timer),它就会越来越快!
setTimeout:表示过了多久之后执行,只会执行一次!
比如这段代码,实现的是每秒都在console里输出现在的时间,5秒后停止。
上代码

<!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>

运行结果:

如果setTimeout设置时间为0呢?
修改一下代码:

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

运行结果:

加上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>

看下结果:

这一段完全没有调用到。

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

为啥呢,因为没有resolve()
加上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>

那是不是什么时候resolve()就什么时候输出呢?
答案是:不是
把“2”换到下面来试一下:

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


可见3和2的位置确实变了,但是promise部分,就是5和6,仍然是最后出现。

那有多个setTimeout会怎样呢?我们加一个“2.5”试一下:

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

输出结果是:

可见是从上至下的啊~

如果让resolve()和console.log(‘3’)摆在一起呢?

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

执行结果:

可见,在setTimeout里套的setTimeout会被最后执行。

如果改一下执行时间呢?把2.5输出的时间改为运行0.1s之后?

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

执行结果:

它会出现在最后!

那如果resolve()执行时间延后呢?

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

运行结果:

该先执行的还是先执行了啊~

加上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') 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>

输出结果是:

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

setTimeout会在async/await之后再执行。但promise的内容会在async/await之前执行。

混乱起来了:把setTimeout塞进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>

输出结果:(看仔细了哦)

所以塞在async/await中的setTimeout会被最后执行。

终极混乱:

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

在Promise里的setTimeout里的setTimeout里的resolve()是什么时候执行的?
完整代码:

<!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>

输出结果:

啊,原来是这样,这一部分会被最后执行!
(作者已经要陷入混乱中了…)

休息一下…接着做~
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>

输出结果:

事实证明,setTimeout是一层一层来的,理解好“第几批”很重要。

可以自己尝试一下,这个如金字塔一样的结构,先搭好第一层地基,才会向上搭建这种思想。

至此你已经看完了所有的内容,感谢观看,实操大于理解,还是建议读者新建一个项目自己动手尝试各种情况,还有更多嵌套情况本文没有列出,如有勤奋的同学列举,欢迎讨论~


补充:

setTimeout并不是准时的原因

  • 因为 JavaScript 是一个单线程序的解释器,因此一定时间内只能执行一段代码。
  • 为了控制要执行的代码,就有一个 JavaScript 任务队列。
  • 这些任务会按照将它们添加到队列的顺序执行。
  • setTimeout() 的第二个参数告诉 JavaScript 再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行