聊聊事件循环

事件循环

> js 是单线程,js 引擎在执行时的原则:获取任务、执行任务。反复重复此过程,直到没有可执行的任务为止。任务分为同步任务和异步任务。异步任务分为宏任务和微任务。

- js 处理异步主要有微任务(microTask)和 宏任务 (macroTask),而从开始执行一个宏任务–>执行完这个宏任务中所有同步代码—>清空当前微任务队列中所有微任务—> UI 渲染 。 这便是完成了一个事件循环(Tick), 然后开始执行下一个宏任务(相当于下一轮循环)。

宏任务

- script(整体代码)
- setTimeout、setInterval
- setImmediate(node)
- requestAnimationFrame
- I/O
- DOM

微任务

- process.nextTick(node)
- Promise.then、Promise.catch、Promise.finally
- MutationObserver(监听 dom 变化)

如何执行

> 首先执行宏任务,在执行宏任务过程中,遇到同步代码立即推入执行栈,遇到微任务追加到微任务队列,遇到宏任务追加到宏任务队列,等待同步任务执行完成后,将微任务队列中的任务依次执行直到微任务队列为空,到此循环结束,下一个循环从宏任务开始执行宏任务重复以上操作。

事件循环机制(node)

- timers 阶段:这个阶段执行 timer(setTimeout、setInterval)的回调
- 定时器检测阶段(timers):本阶段执行 timer 的回调,即 setTimeout、setInterval 里面的回调函数
- I/O 事件回调阶段(I/O callbacks):执行延迟到下一个循环迭代的 I/O 回调,即上一轮循环中未被执行的一些 I/O 回调
- 闲置阶段(idle, prepare):仅系统内部使用
- 轮询阶段(poll):检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞
- 检查阶段(check):setImmediate() 回调函数在这里执行
- 关闭事件回调阶段(close callback):一些关闭的回调函数,如:socket.on('close',...)

setTimeout 与 setInterval 时间不准如何解决

> 定时器指定的时间间隔,表示的是何时将定时器的代码添加到消息队列,而不是何时执行代码。所以真正何时执行代码的时间是不能保证的,取决于何时被主线程的事件循环取到,并执行。setTimeout 低于 4ms 时间间隔为 4ms(不同浏览器有不同的最小时间设定)

setTimeout(js 单线程阻塞,由于前一个任务耗时过长倒是定时器无法立即放入到事件队列)

- 确保回调函数尽量短,避免时间的计算或阻塞操作过长。
- 可以使用 requestAnimationFrame 代替 setInterval
- 使用 setImmediate 代替 setTimeout

setInterval(累计误差、单线程阻塞)

- 使用 setTimeout 代替 setInterval
- 计算每次回调执行的时间

为什么使用 setTimeout 代替 setInterval

- setInterval,某些时间会被跳过(比如上个还是队列中就会被跳过)
- setInterval 可能多个定时器连续触发(上一个在执行,下一个进入队列时间,上一个执行过长下一个直接执行)
- setTimeout 每次产生的任务直接 push 到任务队列,不会出现连续触发
posted @ 2024-09-18 18:32  紫羽35  阅读(5)  评论(0编辑  收藏  举报