# setTimeout
从上一节我们知道了页面是如何运转的,我们知道是由时间循环系统不断的编排工作给主线程来运转的。
- 当收到 HTML 文档,渲染引擎会将解析 DOM 的事件添加到消息队列中。
- 当用户改变窗口大小,渲染引擎会将重新布局的事件添加到消息队列中.
- ...
- 添加到消息队列之后,事件循环系统就会按照消息队列中的顺序来执行事件。
# 浏览器如何实现 setTimeout
在 Chrome 中除了正常使用的消息队列之外,还有另一个消息队列,这个队列维护了需要延迟执行的任务队列标,包括了定时器和 Chromium 内部一些需要延迟执行的任务。
当调用 setTimeout 的时候,渲染进程会为创建一个回调任务,包含了回调函数、发起时间、延迟执行时间。
创建好之后,将任务添加到延迟执行队列中。
# 消息循环系统如何触发延迟队列
会有一个专门用来处理延迟执行任务的函数。处理完消息队列中的一个任务之后,就会开始执行这个函数,这个函数会根据发起时间和延迟时间计算出到期的任务,然后依此执行这些到期的任务。等到期的任务执行完之后,再继续下一个循环过程。
# 清除定时器
设置一个定时器,引擎会返回一个定时器的 ID。 调用 clearTimeout 直接从 延迟队列中,通过 ID 查找到对应的任务,然后再将其从队列中删除。
# setTimeout 注意事项
如果当前任务执行时间过久,会影响到定时器的执行。
如果 setTimeout 存在嵌套,那么系统会设置最短时间间隔 4 毫秒。
前五次调用的时间间隔比较小,嵌套调用超过五次以上,后面每次的调用最小时间间隔是 4 毫秒。在 Chrome 中,定时器被调用五次以上,系统会判定该函数方法被阻塞了,如果定时器的调用时间间隔小于4毫秒,那么浏览器会将每次调用的时间间隔设置为 4 毫秒。
所以对于实时性较高的需求就不太适合使用 setTimeout 。
未激活的页面, setTimeout 执行最小间隔是 1000 毫秒。 如果标签不是当前的激活标签,那么定时器最小的时间间隔是 1000 毫秒,目的就是为了优化后台页面的加载损耗以及降低耗电量。
延时执行时间有最大值 Chrome、Safari、Firefox 都是以 32 个 bit 来存储延迟时值的,32bit最大智能存储的数字 2137483647 毫秒,如果大于就会溢出,那么相当于延迟执行被设置为了 0, 这会导致定时器立即调用。
setTimeout 的回调函数中的 this 不符合直觉 this 会被设置为全局 window。严格模式下,会被设置为 undefined.
那么使用函数包裹,要么使用 bind 来。
# 引出requestAnimationFrame
- requestAnimationFrame 不需要设置具体的时间,由系统来决定回调函数的执行时间。
- requestAnimationFrame 是按照系统刷新的节奏调节的, 保证每个刷新间隔只会执行一次,一般会在 16ms 间隔内根据选择浏览器情况执行相关动作, 。
- requestAnimationFrame 如果页面未激活,raf 也会停止渲染,保证页面的流程性,节省主线程执行函数的开销。
← 异步方案 XMLHttpRequest →