https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ 这个网站可以帮助理解,下面是我的笔记
chrome浏览器每一个tab标签都会开启一个渲染进程,渲染进程会开启一个渲染主线程,这个主线程包含了执行js,css,html解析,dom渲染等。
注意:新的w3c规范已经撤销了宏任务的说法,代替它的是多种任务队列,但是为了解释方便,下面依然使用宏任务,代替这些乱七八糟的任务队列。
渲染主线程会开启一个无限循环执行机制,每次循环执行完都会去询问微任务和其他任务(宏任务)
微任务
(MutationObserver,Promise)的执行优先级是最高的。
其次是用户交互任务,宏任务的一种
(点击等事件)。
最后是定时器任务,宏任务的一种
(setTimeout,setInterval)。
先看微任务队列,如果微任务有东西,会把微任务的所有任务,一次性执行完,
渲染主线程从微队列中拿任务是一个一个拿,还是一次性都拿出来,目前还没找到可靠的说法,但是无论是哪一种机制,执行结果都是相同的,所以这点不用过分纠结,等我找到答案会补充,下面的解释都将使用“一个一个拿”这种机制。
执行过程中如果产生了新的微任务,将会把新的微任务放到微任务队列末尾,并在当前这一轮微任务处理过程中
被执行,而不会等到下一个宏任务。这就是为什么在处理Promise链时,所有的.then回调会在一次事件循环中连续执行,而不会被其他宏任务(如setTimeout)打断。
微任务队列全部执行完页面会重新渲染一次,然后执行宏任务,在 vue 和 react 中,我们经常为了确保一段代码在dom渲染后执行而把代码写在setTimeout中,这就是原因。
如果微任务队列是空的,渲染主线程回去宏任务队列拿一个任务,注意!是一个任务!
requestAnimationFrame 处于渲染阶段,不在微任务队列,也不在宏任务队列
举例说明(下面代码中的【p】是标记,后文会引用)
jsvar a【p1】 = new Promise(res => { setTimeout(res); }); a.then【p2】(() => { console.log(1); }) .then【p3】(() => { console.log(2); }) .then【p4】(() => { console.log(3); }); a.then【p5】(() => { console.log(4); }) .then 【p6】(() => { console.log(5); }) .then 【p7】(() => { console.log(6); }); setTimeout(() => { console.log(7); Promise.resolve().then(() => { console.log(8); }); }); setTimeout(() => { console.log(9); }); Promise.resolve().then(() => { console.log(10); }); console.log(11); // 11,10,1,4,2,5,3,6,7,8,9
下面拆解执行步骤
任务 | 当前执行代码 |
---|---|
渲染主线程 | 全局代码 |
微任务 | |
宏任务 |
全局代码执行顺序
将 setTimeout(res) 丢进定时器任务,p1 = pending,一旦p1的状态为fulfilled,执行的是console.log(1);
jsp1 = pending【 待执行 () => {console.log(1);} 】 p2 = pending【 待执行 () => {console.log(2);} 】 p3 = pending【 待执行 () => {console.log(3);} 】 P4 = pending【 无待执行 】 p1 = pending【 待执行 () => {console.log(1); console.log(4);} 】 p5 = pending【 待执行 () => {console.log(5);} 】 p6 = pending【 待执行 () => {console.log(6);} 】 p7 = pending【 无待执行 】 () => { console.log(7); Promise.resolve().then(() => { console.log(8); }); } 进入定时器任务 () => {console.log(9)} 进入定时器任务 () => {console.log(10)} 进入微队列
console.log(11); 直接执行,此时控制台打印11
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => { console.log(10);} |
宏任务 | res , () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => { console.log(10);} |
微任务 | |
宏任务 | res , () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
执行() => { console.log(10);}, 此时控制台打印 10
渲染主线程执行完代码后,查看微队列,没了,去看宏任务,有东西,拿出第一个来执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | res |
微任务 | |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
res执行后,p1状态变成fufilled,() => {console.log(1);}, () => {console.log(4);} 被放入微队列
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => {console.log(1);}, () => {console.log(4);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(1);} |
微任务 | () => {console.log(4);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
执行() => {console.log(1);}, 此时控制台打印 1
() => {console.log(1);}执行后,p2状态变成fufilled,() => {console.log(2);} 被放入微队列;
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => {console.log(4);} () => {console.log(2);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
去微队列看,发现有,拿出一个来放入主线程执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(4);} |
微任务 | () => {console.log(2);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
执行() => {console.log(4);}, 此时控制台打印 4
() => {console.log(4);}执行后,p5状态变成fufilled,() => {console.log(5);} 被放入微队列;
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => {console.log(2);} () => {console.log(5);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
去微队列看,发现有,拿出一个来放入主线程执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(2);} |
微任务 | () => {console.log(5);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
执行() => {console.log(2);}, 此时控制台打印 2
() => {console.log(2);}执行后,p3状态变成fufilled,() => {console.log(3);} 被放入微队列;
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => {console.log(5);} () => {console.log(3);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
去微队列看,发现有,拿出一个来放入主线程执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(5);} |
微任务 | () => {console.log(3);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
执行() => {console.log(5);}, 此时控制台打印 5
() => {console.log(5);}执行后,p6状态变成fufilled,() => {console.log(6);} 被放入微队列;
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => {console.log(3);} () => {console.log(6);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
去微队列看,发现有,拿出一个来放入主线程执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(3);} |
微任务 | () => {console.log(6);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
执行() => {console.log(3);}, 此时控制台打印 3
() => {console.log(3);}执行后,p4状态变成fufilled,但是p4没有任务放入微队列;
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => {console.log(6);} |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
去微队列看,发现有,拿出一个来放入主线程执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(6);} |
微任务 | |
宏任务 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} () => {console.log(9);} |
执行() => {console.log(6);}, 此时控制台打印 6
() => {console.log(6);}执行后,p7状态变成fufilled,但是p7没有任务放入微队列;
去微队列看,发现没有,去看宏任务,有东西,拿出第一个来执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(7);Promise.resolve().then(() => {console.log(8);});} |
微任务 | |
宏任务 | () => {console.log(9);} |
执行() => {console.log(7);Promise.resolve().then(() => {console.log(8);});} , 此时控制台打印 7
执行后,Promise.resolve() 本身就是 fufilled,() => {console.log(8);}放入微队列;
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | () => {console.log(8);} |
宏任务 | () => {console.log(9);} |
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(8);} |
微任务 | |
宏任务 | () => {console.log(9);} |
执行() => {console.log(8);}, 此时控制台打印 8
执行完去微队列看,发现没有,去看宏任务,有东西,拿出第一个来执行。
任务 | 当前执行代码 |
---|---|
渲染主线程 | () => {console.log(9);} |
微任务 | |
宏任务 |
执行() => {console.log(9);}, 此时控制台打印 9
任务 | 当前执行代码 |
---|---|
渲染主线程 | |
微任务 | |
宏任务 |
最后的练习题
jsPromise.resolve().then(() => { console.log("1"); Promise.resolve().then(() => { console.log("2"); }); }); Promise.resolve().then(() => { console.log("3"); Promise.resolve().then(() => { console.log("4"); }); }); setTimeout(() => { console.log("5"); Promise.resolve().then(() => { console.log("6"); }); }); setTimeout(() => { console.log("7"); });
答案: 1,3,2,4,5,6,7