vaakian / vaakian.github.io

some notes
https://vaakian.github.io
3 stars 0 forks source link

event loop 之 同步、异步、macroTask、microTask #5

Open vaakian opened 4 years ago

vaakian commented 4 years ago

macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering microtasks: process.nextTick, Promise, MutationObserver

同步任务同其他语言一样,拥有调用栈。 当同步任务执行栈空时,JS将microTask队列中的任务依次压入调用栈并执行。 当microTask队列空时,JS将macroTask队列中的任务依次压入调用栈并执行。

先执行完所有同步任务,再检查执行完所有microTask,再挨个执行macroTask

分析题目

setTimeout(()=>{
    console.log('A');
},0);
var obj={
    func:function () {
        setTimeout(function () {
            console.log('B')
        },0);
        return new Promise(function (resolve) {
            console.log('C');
            resolve();
        })
    }
};
obj.func().then(function () {
    console.log('D')
});
console.log('E');

1、首先 setTimeout A 被加入到事件队列中  ==>  此时macrotasks中有[‘A’];

2、obj.func()执行时,setTimeout B 被加入到事件队列中  ==> 此时macrotasks中有[‘A’,‘B’];

3、接着return一个Promise对象,Promise 新建后立即执行 执行console.log('C'); 控制台首次打印‘C’;

4、然后,then方法指定的回调函数,被加入到microtasks队列,将在当前脚本所有同步任务执行完才会执行。 ==> 此时microtasks中有[‘D’];

5、然后继续执行当前脚本的同步任务,故控制台第二次输出‘E’;

6、此时所有同步任务执行完毕,如上所述先检查microtasks队列,完成其中所有任务,故控制台第三次输出‘D’;

7、最后再执行macrotask的任务,并且按照入队列的时间顺序,控制台第四次输出‘A’,控制台第五次输出‘B’。

浏览器render queue重绘优先级

当调用栈有任务时,不会执行重绘。 当事件循环队列有任务时,每执行完一个事件,有一次重绘机会,相当于event queue和render qeue交替执行。

所以如果任务时间开销很大,一定不能放在调用栈里,会阻塞浏览器重绘。

vaakian commented 4 years ago

new Promise传入的函数会立即执行。 同步任务完成->microTask->macroTask

console.log('a') // sync

let promise = new Promise((resolve) => {
    console.log('b') // sync
    setTimeout(() => console.log('e'), 0) // macroTask
    resolve()
})
promise.then(() => console.log('c')).then(() => console.log('f')) // microTask

console.log('d') // sync

结果是先执行同步任务abd,然后微任务 cf,最后宏任务e