EventLoop的全新理解方式
本篇文章将介绍EventLoop的完整机制,重点解释宏任务与微任务的区别和关系,里边涉及一些基础概念如下:
EventTalble 事件注册表,也称作Web APIs,
callback quene 回调队列,异步任务的回调函数会先放进这里再按序执行
script 也就是我们写的同步代码
什么是EventLoop?
- 在执行主线程的任务时,如果有异步任务,会进入到Event Table并注册回调函数,当指定的事情完成后,会将这个回调函数放到 callback queue 中
- 在主线程执行完毕之后,会去读取 callback queue中的回调函数,进入主线程执行
- 不断的重复这个过程,也就是常说的Event Loop(事件循环)了
重点在于异步任务,他们分为宏任务和微任务,两者区别于在指定的事情完成后,将回调函数放入 callback queue 的过程。
简化的模型
我们也不难看出,dom渲染会在微任务队列清空后进行。
宏任务与微任务
我们通常把宿主发起的任务称为宏观任务(
script
其实也算宏任务,但因为他们只执行一遍,所以接下来宏任务里不讨论script
的存在),把JavaScript引擎发起的任务称为微观任务
常见宏任务:setInterval()
,serTimeout()
,setImmediate()
(实际上setImmediate()
优先级小于前者)
常见微任务:Promise().then(function(){})
(promise-then里的回调函数,new MUtationObserver()
process.nextTick(function(){})
(同上)(另外,实际上后者优先级高于前者)
两者在事件轮询中的区别在于,轮询到微任务时,会将微任务队列全部推出再去轮询下一步;轮询到宏任务时,只推出了队列中最早的异步任务
为什么要设计微任务?
解决了宏任务执行时机不可控的问题。微任务宏任务将异步分成了缓急两个任务队列
事件循环闭环流程
我们可以想象成宏任务循环里套着微任务循环,在检查宏任务之前,执行完宏任务之后,分别插入微任务循环。
宏任务过程:
- 选择宏任务队列中最早的任务(任务A)
- 将“当前正在运行的任务”设置为“任务A”
- 运行“任务A”同步代码(也就是运行回调函数)
- 将“当前正在运行的任务”设置为
null
,删除“任务A”,本次Loop结束 - 选择下一个最早的任务,也就是跳转到1,直到宏任务队列清空
微任务过程:
a. 选择微任务队列中最早的任务(任务x)
b. 将“当前正在运行的任务”设置为“任务x”
c. 运行“任务x”同步代码(也就是运行回调函数)
d. 将“当前正在运行的任务”设置为null
,删除“任务x”
e. 选择下一个最早的任务,也就是跳转到1,直到微任务队列清空
浏览器和node的区别
宏任务
\ | 浏览器 | Node |
---|---|---|
I/O | ✅ | ✅ |
setTimeout | ✅ | ✅ |
setInterval | ✅ | ✅ |
setImmediate | ❌ | ✅ |
requestAnimationFrame | ✅ | ✅ |
页面交互:DOM、鼠标、键盘、滚动事件 | ✅ | ✅ |
script | ✅ |
微任务
/ | 浏览器 | Node |
---|---|---|
process.nextTick | ❌ | ✅ |
MutationObserver | ✅ | ❌ |
Promise.then catch finally | ✅ | ✅ |
最后如果有表述不清楚,或者表述错误的地方,欢迎