事件循环、宏任务、微任务一网打尽(附超多经典面试题)


1.宏任务macrotask/Tasks:
(1)种类:
script主代码块、setTimeout 、setInterval 、nodejs的setImmediate 、MessageChannel(react的fiber用到)、postMessage、网络I/O、文件I/O、用户交互的回调等事件、UI渲染事件(DOM解析、布局计算、绘制)等等
(2)宏任务的问题 , 也即为什么要有微任务?
时间粒度比较大 , 执行的时间间隔是不能精确控制的 , 消息队列中就有可能被插入很多系统级的任务 , 对一些高实时性的需求就不太符合了;所有设计了微任务 , 通俗的讲微任务可以插队 , 本轮宏任务最后 , 插在下一轮宏任务之前 , 微任务队列的任务一次性全部执行完;
2.微任务包括microtask/jobs:
(1)种类:

  • 浏览器端有3个1)new Promise.then 回调2)MutationObserver , 监控dom节点变化;MutationObserver使用“异步”+“微任务”的方式 , 替代旧版mutation event这个同步事件 , 异步解决同步操作的性能问题;微任务解决了实时性的问题;3)Object.observe , 已经废弃了 , 用Proxy对象替代;
  • nodejs有2个 , process.nextTick、 new Promise.then 回调

(2)优先级如下:
nodejs中process.nextTick >new Promise.then(回调) > MutationObserver
(3)宏任务和微任务的关系
每个宏任务可以创建自己的一个微任务队列;
事件循环、宏任务、微任务一网打尽(附超多经典面试题)
本文插图
3.一个宏任务微任务的例子 , 理解用户点击事件和JS模拟点击事件的区别
  • 当我们使用 手动点击按钮时 , 浏览器的输出是listener1 -> promise resolved 1 -> listener2 -> promise resolved 2
  • 当我们使用 JS触发点击行为时 , 浏览器的输出是listener1 -> listener2 -> promise resolved 1 -> promise resolved 2原因:
    (1)人工点击

事件循环、宏任务、微任务一网打尽(附超多经典面试题)
本文插图
  • 从上图中我们可以看到 , 一次点击事件之后 , 浏览器会调用 Function Call 进入JS引擎 , 执行 listener1 , 输出 listener1 。
  • 弹栈时发现JS调用栈为空 , 这时候就会执行 Microtasks 队列中的所有 Microtask , 输出 promise resolved1 。
    接着浏览器调用 Function Call 进入JS引擎 , 执行 listener2 , 输出 listener2 。
    弹栈时发现JS调用栈为空 , 这时候就会执行 Microtasks 队列中的所有Microtask , 输出 promise resolved2 。
    (2)JS触发点击事件

事件循环、宏任务、微任务一网打尽(附超多经典面试题)
本文插图