JavaScript Event Loop in Browser
create:January 14, 2022 update:April 12, 2022 • ☕️ 3 min read
同步链接: https://www.shanejix.com/posts/JavaScript Event Loop in Browser/
浏览器(多进程)其实就是是一个异步非阻塞模型的实现,当新建一个Tab 页面时,就会新开一个进程。一个 Tab 页面从加载到渲染需要在浏览器内核(多线程)的调度下控制各线程互相配合以保持同步。浏览器常驻线程包括:GUI 渲染线程,JavaScript 引擎线程,定时触发器线程,事件触发线程,异步 http 请求线程等。
虽然 JavaScript 引擎线程时单线程,但是浏览器时内核是多线程,EventLoop 正是浏览器异步非阻塞模型实现的关键,线程通信的桥梁。
循环阶段:
┌───────────────────────┐
┌─>│ script │
│ └──────────┬────────────┘
│ │
│ microtasts
│ │
│ render
│ │
│ ┌──────────┴────────────┐
│ │ mousemove │
│ └──────────┬────────────┘
│ │
│ microtasts
│ │
│ render
│ │
│ ┌──────────┴────────────┐
└──│ setTimeout │
└───────────────────────┘
简化版事件循环算法:
1. 从 宏任务 队列(例如 “script”)中出队(dequeue)并执行最早的任务
2. 执行所有 微任务:
- 当微任务队列非空时:
- 出队(dequeue)并执行最早的微任务
3. 如果有变更,则将变更渲染(render)出来
4. 如果宏任务队列为空,则休眠直到出现宏任务
5. 转到步骤 1
详细算法流程参见规范:event-loop-processing-model 动态观察 JS Stack
,Tasks Queue
, MicoTasks Queue
的详细变化参见:tasks-microtasks
注意点:
- 微任务会在上一次宏任务中入队或者在执行微任务队列时入队
- 微任务会在执行 [任何其他事件处理,或渲染,或执行任何其他] 宏任务 之前完成(确保了微任务之间的应用程序环境基本相同如没有鼠标坐标更改,没有新的网络数据等)
宏任务(macrotask,也叫 task),由宿主(浏览器、Node)发起。如下类型异步任务的回调会进入 MacroTask Queue
:
- script(整体代码)
- setTimeout
- setInterval
- setImmediate (Node 独有)
- requestAnimationFrame (浏览器独有)
- I/O
- 事件队列
- UI rendering (浏览器独有)
微任务(microtask,也叫 jobs),由 JS 自身发起。 如下类型异步任务的回调会进入 MicroTask Queue
:
- process.nextTick (Node 独有)
- Promise
- async/await(实际就是 promise)
- Object.observe
- MutationObserver(html5 新特性)
总结
一句话概述什么是事件循环:
在每个宏任务执行末尾,会插入微任务队列(微任务产生于当次宏任务或微任务),然后 pop 出宏任务队列继续执行
EventLoop 不仅仅是靠的 JS 执行线程的单独工作,其背后是浏览器多线程的相互协作和通信
references
- https://zh.javascript.info/event-loop
- https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
- https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
作者:shanejix 出处:https://www.shanejix.com/posts/JavaScript Event Loop in Browser/ 版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。 声明:转载请注明出处!