Event Loop, setTimeout, requestA

2018-12-01  本文已影响0人  夏liao夏天

有没有担心过 JS 中执行顺序的问题?如以下的代码,在页面上添加一个 el,并将它隐藏。

document.body.appendChild(el);
el.style.display = 'none';

假如 appendChild()执行得足够快的话,能否在网页上看到一闪而过的 el 呢?答案是不可以的,这与网页的 Main thread 有关。

Main Thread

网页中的main thread 是唯一的,即网页在单线程中运行,无论是 js 的运行,还是页面的渲染,还是 dom 的操作,都是在同一个 main thread 中操作的。代码的运行和页面的渲染顺序是严格定义的并且大多数情况下都是确定的。而这又要归功于 Event Loop

Event Loop

Event Loop 名称来源于它的实现原理

while (queue.waitForMessage()) {
  queue.processNextMessage();
}

在 Event Loop 中有几个重要概念

JS运行时 Event Loop

setTimeout 的执行顺序

setTimeout 就属于 Web APIs,它设置一个计时器,当计时器时间到了,它再执行 callback 函数。

⚠️ 注意:

考虑 setTimeout(callback, ms)的执行顺序:

第一想法是不是这样:

1. 等待ms毫秒

2. 调用callback函数

然后这是不对的,因为 setTimeout 是一个异步函数,它在一个跟 main thread 平行的 thread 中执行,在过了 ms 毫秒之后,再将 callback 函数以任务的形式加入到队列中,因此真正的执行顺序为:

1.在一个平行的线程中执行以下步骤:

  1. 等待ms毫秒

  2. 将callback函数以任务的形式加入到队列中,执行以下步骤:

     1.调用callback函数

Task queues

如下图片所示,假如将电脑环境比作一个圆环形的管道,只要 cpu 在正常运行,Event Loop 就是管道中的那个小方块, 不停地转啊转。

Event Loop

当在 queue 中加入一个 task 时,就相当于给 Event Loop 多加了一段支路。在某个时间点,浏览器跟 Event Loop 说,老兄,我这有 task 需要完成。Event Loop 回复说,没问题,把它加到我的 todo 列表中。

考虑执行以下两个函数:

setTimeout(callback1, 1000);
setTimeout(callback2, 1000);

如前面所说,这两个函数先分别在两个平行的 thread 中执行,当时间到了 1000ms 后,它们回到 main thread,把task 放入 queue 中。这时候浏览器告诉 Event Loop,我这有两个 task 需要在 main thread 中执行。Event Loop 就依次执行两个 task。


两个task处于queue中
执行第一个task
还剩一个task
执行第二个task

The Render Steps

当要考虑到渲染的时候,就更加复杂了。即浏览器考虑是否更新当前显示的页面。


Event Loop

渲染就相当于又多加了一条支路,这条支路上有三个 task:

假如 浏览器需要 更新页面的时候,它就会对 Event Loop 说,老兄,我需要你帮忙更新页面,这个时候 Event Loop 就会走页面更新的那条支路。


Event Loop

RequestAnimationFrame 的执行顺序

requestAnimationFrame() 方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。

Event Loop

由上图可知,RequestAnimationFrame 函数在渲染的部分执行,这样当浏览器想要更新动画的时候,无需先将 task 加入 queue 中,再告诉 Event Loop 需要更新页面。加快了动画更新的响应速度。

⚠️ 注意:并不是所有的浏览器都像 Chrome 和 FireFox 将 RAF 函数放在 render 的第一步执行,有些浏览器将它放到了 Pixel Data 之后执行。虽然这样做是不正确和不好的,因为已经把页面渲染 完成了,在执行 RAF 函数,那它的效果只能在下一桢看到了。


Event Loop

参考资料

上一篇 下一篇

猜你喜欢

热点阅读