EventLoop

1. EventLoop的执行流程图

  ┌───────────────────────┐
┌─>│ timers │<————— 执行 setTimeout()、setInterval() 的回调
│ └──────────┬────────────┘
| |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│ ┌──────────┴────────────┐
│ │ pending callbacks │<————— 执行由上一个 Tick 延迟下来的 I/O 回调(待完善,可忽略)
│ └──────────┬────────────┘
| |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│ ┌──────────┴────────────┐
│ │ idle, prepare │<————— 内部调用(可忽略)
│ └──────────┬────────────┘
| |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
| | ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │ - (执行几乎所有的回调,除了 close callbacks 以及 timers 调度的回调和 setImmediate() 调度的回调,在恰当的时机将会阻塞在此阶段)
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ | | |
| | └───────────────┘
| |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
| ┌──────────┴────────────┐
│ │ check │<————— setImmediate() 的回调将会在这个阶段执行
│ └──────────┬────────────┘
| |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│ ┌──────────┴────────────┐
└──┤ close callbacks │<————— socket.on('close', ...)
  1. setTimeout/setInterval 属于 timers 类型;
  2. setImmediate 属于 check 类型;
  3. socket 的 close 事件属于 close callbacks 类型;
  4. 其他 MacroTask 都属于 poll 类型。
  5. process.nextTick 本质上属于 MicroTask,但是它先于所有其他 MicroTask 执行;
  6. 所有 MicroTask 的执行时机,是不同类型 MacroTask 切换的时候。
  7. idle/prepare 仅供内部调用,我们可以忽略。
  8. pending callbacks 不太常见,我们也可以忽略。

2. 执行机制

  1. 先执行所有类型为 timers 的 MacroTask,然后执行所有的 MicroTask(注意 NextTick 要优先哦);
  2. 进入 poll 阶段,执行几乎所有 MacroTask,然后执行所有的 MicroTask;
  3. 再执行所有类型为 check 的 MacroTask,然后执行所有的 MicroTask;
  4. 再执行所有类型为 close callbacks 的 MacroTask,然后执行所有的 MicroTask;
  5. 至此,完成一个 Tick,回到 timers 阶段;

    ……

    如此反复,无穷无尽……

2.1 实例理解

const macroTaskList = [
['task1'],
['task2', 'task3'],
['task4'],
] for (let macroIndex = 0; macroIndex < macroTaskList.length; macroIndex++) {
const microTaskList = macroTaskList[macroIndex] for (let microIndex = 0; microIndex < microTaskList.length; microIndex++) {
const microTask = microTaskList[microIndex] // 添加一个微任务
if (microIndex === 1) microTaskList.push('special micro task') // 执行任务
console.log(microTask)
} // 添加一个宏任务
if (macroIndex === 2) macroTaskList.push(['special macro task'])
} // 输出结果:
// > task1
// > task2
// > task3
// > special micro task
// > task4
// > special macro task

2.2 执行细节分析

2.2.1 试分析下面程序的执行结果

console.log(1)
setTimeout(function() {
console.log(2)
}) Promise.resolve()
.then(function() {
console.log(3)
}) console.log(4)

2.2.2 执行流程分析

stack(执行栈)、Micro(微任务)、Macro(宏任务)

  1. 初始状态: stack:[], Micro: [], Macro: [script]。执行栈为空, 微任务为空, 宏任务队列中有一个整体的 script代码

  2. 主线程开始执行, 遇到console.log(1), 首先会打印 1

  3. 继续向下执行,遇到 setTimeout异步任务,就将其加入到Macro(宏任务)队列中。等待执行

  4. 继续向下执行, 遇到 Promise.resolve也是一个异步任务,单它是微任务,将其加入 Micro(微任务)队列中,等待着行

  5. 解析console.log(4), 并且打印4。 当主线程执行完打印的结果依次是 1 和 4。

  6. 这时候主线程就会问 任务(异步)队列,有没有微任务要执行,将所有的 Micro(微任务)加入执行栈执行, 打印结果 3

  7. 微任务执行完了, 就开始下一轮事件循环, 将第一个 Macro(宏任务)压入执行栈执行, 再次打印 2。

3. 谈一下宏任务与微任务的区别?(面试重点)

[!NOTE]

面试常考点,关键在于理解EventLoop的机制,以及宏任务和微任务的底层原理。

3.1 宏任务

  • setTimeout
  • setInterval
  • setImmediate
  • requestAnimationFrame

常见的宏任务: setTimeout、setInterval、setImmediate、 script中整体的代码、 I/O操作、 UI渲染等。

3.2 微任务

  • process.nextTick
  • MutationObserver
  • Promise.then catch finally

常见的微任务有: process.nextTick、Promise和 MutationObserver监听DOM的变化。

3.3 微任务和宏任务的区别

[!NOTE]

  • 微任务进入主线程执行是一队一队的, 而宏任务进入主线程是一个一个的。
  • 微任务是在主线程空闲时批量执行, 宏任务是在事件循环下一轮的最开始执行

参考文章:

最新文章

  1. Bootstrap之选项卡
  2. java位操作总结
  3. KnockoutJS 3.X API 第五章 高级应用(1) 创建自定义绑定
  4. Makefile_:=与=的区别
  5. eclipse生成doc文件乱码
  6. 模仿angularjs写了一个简单的HTML模版和js数据填充的示例
  7. Linux安装snmp
  8. 路由器之VPN应用与配置指南
  9. linux下显卡信息的查看
  10. windows 2003 域控制器(AD)的常规命令行操作以及修复
  11. 用spark-submit启动程序
  12. JS中常见设计模式总结
  13. JPA、Hibernate框架、通用mapper
  14. python标签值标准化到[0-(#class-1)]
  15. Java实现三大简单排序算法
  16. windows下安装ElasticSearch的Head插件
  17. 报错解决——ctypes.ArgumentError: argument 1:……….. : wrong type
  18. Spring MVC POM示例
  19. Golang数组Array
  20. \r\n 如何转换成utf-8格式的,在jsp页面中正常显示换行

热门文章

  1. 安装完dlib以及face_recognition,但是在python IDE中无法导入的问题
  2. Jsoup解析Xml{详解}
  3. 02-Nginx配置
  4. 高并发 Nginx+Lua OpenResty系列(11)——流量复制/AB测试/协程
  5. Selenium(十九):unittest单元测试框架(五) Page Object设计模式
  6. Add an Action with Option Selection 添加具有选项选择的按钮
  7. socket之线程来提高吞吐量
  8. 个人项目-WC(C/C++ 兼容Linux和Windows)
  9. Saltstack_使用指南12_配置管理-jinja模板
  10. 纸壳CMS现已支持自定义扩展字段