最近的一道前端面试题,让我对js事件循环(Event Loop)的理解

236 阅读1分钟

什么是js事件循环?

javascript是单线程的,执行代码的时候会逐行的执行,碰到异步代码(发起ajax请求,执行setTimeout)会挂起放入到消息队列里,继续执行后边的代码,执行完毕以后才会执行消息队列的代码(面试之前的理解)

测试代码

<script>
    console.log('start')
    setTimeout(() => {
      console.log('setTimeout start')
    }, 0)
    for (var i = 0; i < 10; i++) {
      console.log('循环执行')
    }
    const p1 = new Promise((resolve, reject) => {
      console.log('构造函数执行')
      resolve(1)
    }).then(res => {
      console.log('then1', res)
      return res
    }).catch(err => {
      console.log('err', err);
    })

    Promise.resolve(2).then(_ => console.log('then2', 2))
    console.log('end')
  </script>

打印结果:

代码改造为中途添加点击任务

为了效果明显,我把定时器时间改为4s后运行,for循环改为3000次,当我们重新执行一次代码,并且连续点击按钮

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button onclick="onclick1()">按钮</button>
  <script>
    console.log('start')
    
    function onclick1() {
      console.log('按钮点击了');
    }
    
   setTimeout(() => {
        console.log('setTimeout start1')
      }, 1000)
      
    setTimeout(() => {
      console.log('setTimeout start2')
    }, 4000)
    
    for (var i = 0; i < 3000; i++) {
      console.log('循环执行')
    }
    const p1 = new Promise((resolve, reject) => {
      console.log('构造函数执行')
      resolve(1)
    }).then(res => {
      console.log('then1', res)
      return res
    }).catch(err => {
      console.log('err', err);
    })

    Promise.resolve(2).then(_ => console.log('then2', 2))
    console.log('end')
  </script>
</body>

</html>

打印结果如下:

总结

  • javascript是单线程的,执行代码的时候碰到异步代码会挂起,继续执行后边的代码
  • setTimeout,setInterval的回调函数会加入到宏任务的消息队列
  • Promise的then()方法里边的函数会加入到微任务的消息队列
  • 在一次事件循环里,当主线程空闲时(执行栈清空了),会优先执行微任务消息队列的任务,直到清空微任务队列
  • 微任务的队列清空以后,主线程会执行宏任务队列里边满足当次循环的任务,不一定清空队列,例如设置4s的定时器就不在这次的事件循环执行,我们点击13次按钮后才打印的定时器,说明在第13次事件循环执行的第二个定时器的宏任务