JS 执行机制

浏览器和 NodeJS 执行机制
浏览器执行机制图



NodeJS 执行机制图

阶段概述:
每个框被称为事件循环机制的一个阶段, 每个阶段都有一个 FIFO 队列来执行回调
- 定时器:本阶段执行已经被
setTimeout()
和setInterval()
的调度回调函数。
- 待定回调:执行延迟到下一个循环迭代的 I/O 回调。
- idle, prepare:仅系统内部使用。
- 轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和
setImmediate()
调度的之外),其余情况 node 将在适当的时候在此阻塞。
- 检测:
setImmediate()
回调函数在这里执行。
- 关闭的回调函数:一些关闭的回调函数,如:
socket.on('close', ...)
。
macroTaskQueue/microTaskQueue/nextTickQueue 执行顺序
浏览器环境
setImmediate 不是浏览器标准功能, 兼容性不好
- 宏任务: setTimeout, setInterval, I/O,setImmediate(如果存在, 目前只有 Edge 和 IE11 支持),requestAnimationFrame(存在争议)
- 微任务: Promises,MutationObserver
总结:
- 浏览器环境下, 会依次执行
宏任务 -> 微任务栈 -> 宏任务 -> 微任务栈
NodeJS 环境
NodeJS 有主执行线程, I/O线程, worker_threads 线程, 没有定时器线程, 也没有宏任务栈的概念, 但是在 NodeJS 11 的某个版本之后, 主动向浏览器的逻辑靠拢, 导致可以近似理解为 NodeJS 是有宏任务栈的
- 阶段任务:setTimeout, setInterval, setImmediate
- 微任务:process.nextTick, promise.then
总结:
- NodeJS 11 的某个版本之前: 在每个
timmer
和immediate
执行之后, 并不会执行nextTick栈 → 微任务栈
, 而是会在每个阶段结束前统一清空
- NodeJS 11 的某个版本之后: 在每个
timmer
和immediate
执行之后, 都会执行nextTick栈 → 微任务栈
, 为了与浏览器事件机制保持一致
复现代码
相关 Issue:
- timers: run nextTicks after each immediate and timer #22842.
- MacroTask and MicroTask execution order #22257.
- implement queueMicrotask #22951.
宏任务微任务兼容
微任务基本原理
核心微任务代码实现见
core-js
的 microtask
模块- 优先使用
queueMicrotask
方法
- 浏览器可以使用
MutationObserver
, 除了 iOS 环境
- 有些环境实现了非完全正确的
Promise
(例如WebKit ~ iOS Safari 10.1
), 可以使用Promise.then
实现
NodeJS
使用process.nextTick
- 其他环境使用
macrotask
实现
宏任务基本原理
核心微任务代码实现见
core-js
的 task
模块- 优先使用
setImmediate
和clearImmediate
- 早期 NodeJS 0.8 - 没有
immediate
, 使用process.nextTick
- Sphere (JS game engine) 使用
Dispatch.now
- 浏览器使用
MessageChannel
除了 iOS 环境
- 其他浏览器(除了 IE8 -), 使用
global.postMessage
和global.addEventListener('message', ...)
- 其他浏览器使用
html.appendChild(createElement('script'))['onreadystatechange']
- 其余的
setTimeout
兜底
特殊示例
关于 setTimeout/setInterval 0 延迟
The
HTML5 standard
says: “after five nested timers, the interval is forced to be at least 4 milliseconds.”结果说明:
- 在浏览器中如果 setTimeout 被嵌套了 5 次, 之后的回调间隔至少为 4ms
- 在 NodeJS 中执行没有这个限制
原因分析:
- 如果没有延迟有可能会导致 UI 锁死, CPU 过载
- 操作系统的 clock 本身也不是很准确
相关代码: