讲讲 EventLoop 以及任务源

讲讲 EventLoop 以及任务源

本文参考:深入探究 eventloop 与浏览器渲染的时序问题

EventLoop

EventLoop 也叫事件循环,是浏览器主线程的执行机制,用来协调浏览器各部分的工作,如 DOM 事件、用户交互、脚本、UI 渲染、网络请求等等

这些各部分的工作,基本都会作为任务源产生一个 task 队列,等待事件循环去处理

而事件循环则类似于一个 while(true) 的死循环,不断的重复协调工作,以轮为单位,所以有的文章会有每一个 eventloop 这种说法,其实也就是想表明在一轮事件循环中,浏览器都需要处理哪些事:

  1. 选择优先级高的 task 队列执行
  2. 清空 microtask 队列
  3. 执行 UI Render(有必要的话)

简单来说,就是任务源会产生宏任务 task 队列,事件循环在每一轮中,会在多个 task 队列中先挑一个处理,执行完宏任务后,再去清空微任务队列,最后去处理渲染工作

不同任务源产生的 task 可能会被放入不同的队列中,不同队列有优先级之分

任务源和宏任务 task

这里涉及任务源和宏任务 task 两种概念,简单来说,任务源是指某类工作,而 task 则是具体到代码层面的某个工作,通常是回调函数,如:

任务源包括:

  • DOM 操作任务源
  • 用户交互任务源,如鼠标键盘点击事件等
  • 网络任务源,如 XHR 回调
  • history 回溯任务源
  • setTimeout 及数据库操作等任务源

宏任务 task 则是:

  • DOM 绑定的事件回调函数

  • XHR 注册的回调函数

  • setTimeout 注册的回调函数

微任务 microtask

前端中里会产生微任务的,常见的是 Promise 的回调

微任务关键的点在于它的执行时机,通常来说,在每一轮的 eventloop 中,处理完宏任务后,就会去处理微任务队列

有个现象则是,不管是处于执行宏任务还是处于执行微任务期间,再次产生的微任务都会直接丢到当前微任务队列末尾,直接在当前轮就被处理

另外,上面只是微任务被处理的一种场景,微任务队列其实是见缝插针,只要有机会,浏览器就会去清空,比如执行完 script 脚本代码后,js 执行栈空闲了,就会去清理微任务

渲染工作

UI Render 渲染阶段是在一轮 eventloop 中最后被处理的事情,但它并不是每一轮都会被触发执行

因为渲染工作要做的事就是计算渲染树、触发 resize、scroll 事件、建立媒体查询、运行 CSS 动画等等,这些会导致界面刷新的,其实只要保证一定的频率如 60Hz 就可以了

而 eventloop 每一轮处理的工作量、耗时是不固定且非常快的,那么,是没必要每一次都去进行渲染工作的,即使渲染了,浏览器也无法及时显示,所以保证一定频率即可

请叫我大苏 wechat
您的支持将鼓励我继续创作!