简单学习一下事件循环、宏任务和微任务

122 阅读2分钟

首先,JavaScript是单线程(为什么是单线程,因为在操作dom的时候保持单一原则),这意味着在任何时候js只有一个调用栈,一个时刻也只能执行一个任务。

      function foo1() {
        console.log('foo1')
      }
      function foo2() {
        foo1()
        console.log('foo2')
      }
      function foo3() {
        foo2()
        console.log('foo3')
      }
      foo3()
      // 输出:foo1 foo2 foo3

这一段函数就是比较好的例子,有点类似于一条路走到黑,一直执行到底。

如果js执行一个很慢的任务,比如网络请求,定时器。作为单线程的js如果同步执行这些代码的话,会阻塞其他的任务,所以这些函数一般都被设计成异步执行的,但js又是单线程的,怎么办呢?

好在浏览器提供了运行时候的环境,即是一个专门为异步函数执行时候的数据结构,事件队列。当执行一个定时器的时候,回调函数会有浏览器专门的定时器线程为你计时,当时间结束再将你的回调函数放入事件队列。当主线程,即是你的调用栈中其他任务执行完成后,将事件队列头部的事件加入调用栈中被执行。这就是为什么会将比如网络请求这样的费时容易阻塞线程执行的事件设为异步执行了,不会阻塞其他事件执行,也不会阻塞页面的重绘渲染。

说简单点,事件循环就是执行调用栈中的任务以及事件队列的任务,再在两个之间反复横跳。其中优先执行调用栈中的任务,这不就是循环嘛,当然,这其中也存在没任务可做啥事不干的时候。

但是事件队列又有细分,即是宏任务队列以及微任务队列。

微任务队列包括了promise得到catchthenfinally方法,以及一个特殊的方法,queueMicrotask(foo),foo函数就会被放入微任务队列中。

宏任务队列一般接触的就是两个定时函数,setTimeoutsetInterval

并且每次执行宏任务之前都会清空微任务队列中的任务,我理解就是微任务优先级更高。

      console.log('1')
      // 宏任务
      setTimeout(() => {
        console.log('4')
      }, 0)
      // 微任务
      Promise.resolve().then(() => {
        console.log('3')
      })
      console.log('2')
      // 输出:1 2 3 4

鄙人才疏学浅也想不明白为什么为什么还有这个区分,可能就是vip1和vip9的的区别吧。

总的来说,事件循环就是在执行主程序和队列中的事件之间反复横跳,其中这两者之间优先执行主程序任务,再执行事件队列中的任务;而执行事件队列中的任务时候又优先执行微任务队列中的任务。