JS事件循环,MACRO TASK,MICRO TASK
事件循环的基本概念
- JS执行的过程中,由JS引擎控制的函数调用栈来控制时间循环
- 定时器线程,事件触发线程,异步http请求线程控制异步的任务队列
- 任务分为macro task,micro task 对应都有不同的任务队列
- macro task:script正常代码,setTimeout,setInterval,I/O,UI rendering
- 由事件触发线程维护
- micro task:process.nextTick,promise,mutationObserve
- 由JS引擎线程维护
- 最终在函数调用栈中完成
- macro task:script正常代码,setTimeout,setInterval,I/O,UI rendering
事件循环执行的顺序
- 执行函数调用栈中的macro task,直到调用栈清空(剩下全局)
- 执行job queues中所有可执行的micro tasks
- 执行UI render
- 从事件队列中获取macro task,开始新的事件循环
例子
<div class="outer" style="width:200px;height:200px;background-color: #ccc">
1
<div class="inner" style="width:100px;height:100px;background-color: #ddd">begin</div>
</div>
// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner'); var i = 0; // Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {
console.log('mutate');
}).observe(outer, {
attributes: true
}); // Here's a click listener…
function onClick() {
i++; if(i === 1) {
inner.innerHTML = 'end';
} console.log('click'); setTimeout(function() {
alert('锚点');
console.log('timeout');
}, 0); Promise.resolve().then(function() {
console.log('promise');
}); outer.setAttribute('data-random', Math.random());
} // …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
当我们点击 inner div 时程序依次的执行顺序是:
- onclick 入 JS stack
- 打印出 click
- 将 timeout 压入到 macrotask
- 将 promise 压入到 microtask
- 修改 outer 属性 data-random
- 将 mutate 压入到 microtask,
- onclick 出 JS stack
此时,由于用户点击事件onclick产生的macrotask执行完毕,JS stack 清空,开始执行microtask.
- promise 入 JS stack
- 打印出 promise
- promise 出 JS stack
- mutate 入 JS stack
- 打印出 mutate
- mutate 出 JS stack
此时,microtask 执行完毕,JS stack 清空,但是由于事件冒泡,接着执行outer上的onclick事件.
- onclick 入 JS stack
- 打印出 click
- 将 timeout 压入到 macrotask
- 将 promise 压入到 microtask
- 修改 outer 属性 data-random
- 将 mutate 压入到 microtask,
- onclick 出 JS stack
此时,由于outer上的onclick事件产生的macrotask执行完毕,JS stack 清空,开始执行microtask.
- promise 入 JS stack
- 打印出 promise
- promise 出 JS stack
- mutate 入 JS stack
- 打印出 mutate
- mutate 出 JS stack
此时,本轮事件循环结束,UI 开始 render.
- 页面中inner的innerHTML变为end
此时,UI render 完毕,开始下一轮事件循环.
- timeout 入 JS stack
- 弹出警告 锚点.
- 打印出 timeout
- timeout 出 JS stack
- timeout 入 JS stack
- 弹出警告 锚点.
- 打印出 timeout
- timeout 出 JS stack
到此为止,整个事件执行完毕,我们可以看到在弹出警告框之前inner的内容已经改变。
那如果不是用户点击事件触发onclick,而是js触发呢?
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
inner.click();
此时的执行顺序是:
- 首先是script(整体代码)入 JS stack
- onclick 入 JS stack
- 打印出 click
- 将 timeout 压入到 macrotask
- 将 promise 压入到 microtask
- 修改 outer 属性 data-random
- 将 mutate 压入到 microtask,
- onclick 出 JS stack
此时,inner 的 onclick 已经出 JS stack,但是script(整体代码)还没有出 JS stack,还不能执行microtask,由于冒泡,接着执行 outer 的 onclick.
- onclick 入 JS stack
- 打印出 click
- 将 timeout 压入到 macrotask
- 将 promise 压入到 microtask
- 修改 outer 属性 data-random
接着执行的outer.setAttribute('data-random', Math.random());,但是由于上一个mutation microtask还处于等待状态,不能再添加mutation microtask,所以这里不会将 mutate 压入到 microtask。接着执行:
- onclick 出 JS stack
- script(整体代码)出 JS stack
此时,inner.click()执行完毕,script(整体代码)已出 JS stack,JS stack 清空,开始执行mircotask.
- promise 入 JS stack
- 打印出 promise
- promise 出 JS stack
- mutate 入 JS stack
- 打印出 mutate
- mutate 出 JS stack
- promise 入 JS stack
- 打印出 promise
- promise 出 JS stack
此时,所有的mircotask执行完毕,本轮事件循环结束,UI 开始 render.
- 页面中inner的innerHTML变为end
此时,UI render 完毕,开始下一轮事件循环.
- timeout 入 JS stack
- 弹出警告 锚点.
- 打印出 timeout
- timeout 出 JS stack
- timeout 入 JS stack
- 弹出警告 锚点.
- 打印出 timeout
- timeout 出 JS stack
到此为止,整个事件执行完毕,我们可以看到在弹出警告框之前inner的内容已经改变。
参考文献:
https://segmentfault.com/a/1190000013212944
http://zhangxiang958.github.io/2018/02/03/Event%20Loop%20中的%20microtask%20与%20macrotask/
最新文章
- 9_bootstrap less 移动端
- ssh 文件权限影响登录
- iOS 阶段学习第11天笔记(OC基础知识)
- [转]C#编程总结(三)线程同步
- oracle sqlplus存储过程控制台输出信息
- android wifi驱动移植详细过程
- 开扒php内核函数,第一篇 bin2hex
- 函数buf_page_get
- 在ef下使用lambda实现left join
- AltiumDesigner导入AutoCAD文件DXF,DWG格式
- String,StringBuffer与StringBuilder的差别??
- IO通信
- 理解的javascript自定义事件
- Modules和Autolinking
- css中设置div水平居中,margin:0px auto无用的情况
- 【NOIP2017 OFO(下)】
- Java基础 -- 连接字符串时,使用+还是StringBuilder
- C#编程高并发的几种处理方法
- js控制easyui文本框例子及控制html例子
- Feign Hystrix