https://segmentfault.com/a/1190000014940904基本知识:
1.浏览器引擎
浏览器内核是一个多线程处理,主要包含以下线程:
a.GUI渲染线程:渲染页面的html元素;
b.JavaScript引擎线程:页面的交互和dom渲染;
c.定时触发器线程:一定时间后,来触发对应的线程;
d.事件触发线程:当一个事件触发该线程的时候,就会把它放到js的事件队列中等待还行。常用于异步操作;
e.异步http线程:在ajax操作中通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到JavaScript引擎的处理队列中等待处理;
上诉线程之间有一定的联系:
1.JavaScript引擎和GUI互斥,不能一边操作dom一边渲染页面;
2.JavaScript引擎是单线程,所以需要按照事件处理队列来处理相应的代码;
3.JavaScript引擎有一个监听事件的功能,会持续不断地检查js引擎的主线程执行栈是否为空,如果为空就会去取事件触发线程存放在事件队列中的回调函数执行。
2.js引擎执行机制
由于js的运行环境是单线程,一些异步操作还是需要借助于浏览器这个宿主来实现。下图描述了js运行时候的流程,主要运用了浏览器的js引擎线程和事件触发线程有时开启网络服务和定时器䧥用到其他的线程;
1.Eventloop:是一个程序结构,用于等待和发送消息和时间。简单的说,就是在程序中设置两个线程:一个负责程序本身的运行,称为“主线程”;另一个负责主线程与其他进程(主要是何种I/O操作)的通信,被称为“Event Loop线程”.
2.MacroTask
浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个task执行开始前,对页面进行重新渲染(task->render->task->...)
鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析html。还有下面这个例子,setTimeout ;setTimeout的作用是等待给定的时间后为它的回调产生一个新的宏任务。这就是为什么打印'setTimeout'在'script end'之后,因为打印'script end'是在第一个宏任务里面的事情,而'setTimeout'是另一个独立的任务里面打印的。
主要包括
- setTimeout;
- serInterval;
- I/O
- script代码块
3.MicroTask
微任务通常来说就是需要在当前task执行结束后立即执行的任务,比如对一系列动作做出反馈,或者是需要异步的执行任务而又不需要分配一个新的task,这样便可以减小一点性能的开销。只要执行栈中没有其他的js代码正在执行且每个宏任务执行完,微任务队列会立即执行。如果在微任务执行期间微任务队列还加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。
主要包括:
- nextTick
- callback;
- Promise
- process.nextTick
- Object.observe
- MutationObserver
4.DEMO分析
<div class="outer">
<div class="inner"></div>
</div>
编写如下的js执行代码:
// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');
// Let's listen for attribute changes on the
// outer element
//监听element属性变化
new MutationObserver(function() {
console.log('mutate');
}).observe(outer, {
attributes: true
});
// Here's a click listener…
function onClick() {
console.log('click');
setTimeout(function() {
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);
输出结果如下:
click
promise
mutate
click
promise
mutate
timeout
timeout
element元素的click绑定了事件,当鼠标点击会触发一个事件回调;
当使用inner.click触发事件的时候
输入结果如下:
click
click
promise
mutate
promise
timeout
timeout
之前的例子,微任务会在监听器回调之间执行,下面的例子,click()会导致事件同步分发,所以在监听器回调之间js执行栈不为空,而上诉的这个规则保证了微任务不会打断正在执行的js,这意味着我们不能再监听器回调之间执行微任务,微任务会在监听器之后执行。
备注
- 宏任务按顺序执行,且浏览器在每个宏任务之间渲染页面
- 所有微任务也按顺序执行,且在以下场景会立即执行所有微任务
a.每个回调之后且js执行栈中为空
b.每个宏任务结束之后
参考链接:
google大神解析 https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?