JS事件循环
什么是事件循环
每一个浏览器标签页都会有一个渲染主线程用于专门执行JS任务,JS的任务被分为不同的类型(微任务、延时任务、用户交互任务),不同类型的任务会被存放到不同的队列当中,当主线程空闲时,会根据任务级别去对应的队列中取出,然后执行。
代码示例-1
console.log('Step 1');
setTimeout(() => { console.log('Step 2');}, 0);
Promise.resolve().then(() => { console.log('Step 3');}).then(() => { console.log('Step 4');});
queueMicrotask(() => { console.log('Step 5');});
setTimeout(() => { console.log('Step 6');}, 0);
Promise.resolve().then(() => { console.log('Step 7');});
console.log('Step 8');代码分析-1
渲染主线程(执行全局JS)
// 代码中的全局JS有console.log("Step 1") // 执行完毕-1console.log("Step 8") // 执行完毕-2// 所以直接执行// 此刻已经将所有的任务添加到了对应的队列当中,当主线程空闲时(执行完全局JS代码后),根据实际情况,也就是任务的优先级,去队列中取出任务,执行;// 开始执行微队列任务console.log("Step3"); // 产生了 Step4 的微任务,排到微队列最后执行console.log("Step5");console.log("Step7");console.log("Step4");// end 此刻微队列全部执行完毕// 开始执行延时队列任务console.log("Step2");console.log("Step6");微队列(优先级最高)
Step3(等待执行) - 执行完毕-3Step5(等待执行) - 执行完毕-4Step7(等待执行) - 执行完毕-5Step4(等待执行) - 执行完毕-6用户交互队列(优先级高)
空延时队列(优先级最低)
Step2(等待执行) - 执行完毕-7Step6(等待执行) - 执行完毕-8其他线程(时刻监听是否有延时任务、用户点击、微任务)
Step2 0 秒后到达,将该任务添加到延时队列中等待执行Step3 将该任务添加到微队列中等待执行Step5 会创建一个微任务,添加到微队列Step6 添加到延时队列Step7 将该任务添加到微队列中等待执行执行结果:Step 1Step 8Step 3Step 5Step 7Step 4Step 2Step 6代码示例-2
console.log('A');
setTimeout(() => { console.log('B'); Promise.resolve().then(() => { console.log('C'); });}, 0);
Promise.resolve().then(() => { console.log('D'); setTimeout(() => { console.log('E'); }, 0);}).then(() => { console.log('F');});
queueMicrotask(() => { console.log('G');});
Promise.resolve().then(() => { console.log('H');});
setTimeout(() => { console.log('I');}, 0);
console.log('J');代码分析-2
渲染主线程(执行全局JS)
console.log("A");console.log("J");// 执行全局JS后,主线程空闲,取队列任务执行// P1执行; 输出 'D' 产生E的延时任务和F的微任务,放到对应的队列中,P1执行完毕// Q1执行; 输出 'G'// P2执行; 输出 'H'// P3执行; 输出 'F'// S1执行; 输出 'B' 产生了C的微任务,放到对应的队列中,S1执行完毕// P4执行; 输出 'C'// S2执行; 输出 'I'// S3执行; 输出 'E'微队列(优先级最高)
P1(等待执行) - 执行完毕Q1(等待执行) - 执行完毕P2(等待执行) - 执行完毕P3(等待执行) - 执行完毕P4(等待执行) - 执行完毕用户交互队列(优先级高)
空延时队列(优先级最低)
S1(等待执行) - 执行完毕S2(等待执行) - 执行完毕S3(等待执行) - 执行完毕其他线程(时刻监听是否有延时任务、用户点击、微任务)
1. 第一个 setTimeout(() => { console.log('B'); Promise.resolve().then(() => { P4 console.log('C'); });}, 0); 临时命名为 S1 将回调函数放到延时队列中等待执行
2. Promise.resolve().then(() => { console.log('D'); setTimeout(() => { S3 console.log('E'); }, 0);}).then(() => { P3 console.log('F');}); 临时命名为 P1, 产生微任务,将.then的回调函数放到微队列等待执行
3. queueMicrotask(() => { console.log('G');}); 产生微任务 Q1
4. Promise.resolve().then(() => { console.log('H');}); 产生微任务 P2
5. setTimeout(() => { console.log('I');}, 0); 产生延时任务 S2执行结果:AJDGHFBCIE代码示例-3
接下来结合await/async再去分析
console.log('A');
setTimeout(() => { console.log('B'); Promise.resolve().then(() => { console.log('C'); });}, 0);
async function asyncFunc() { console.log('D');
await Promise.resolve();
console.log('E');}
asyncFunc();
Promise.resolve().then(() => { console.log('F');}).then(() => { console.log('G');});
setTimeout(() => { console.log('H');}, 0);
Promise.resolve().then(() => { console.log('I');});
console.log('J');代码分析-3
渲染主线程(执行全局JS)
// console.log('A'); A 执行-1// asyncFunc()调用; D 执行-2,产生了 await 将 后续的 E 添加到微队列中// console.log('J'); J// E// F 产生了 G 添加到微队列// I// G// B// C// H微队列(优先级最高)
E(等待执行) - 执行完毕F(等待执行) - 执行完毕I(等待执行) - 执行完毕G(等待执行) - 执行完毕用户交互队列(优先级高)
空延时队列(优先级最低)
S1(等待执行)H(等待执行)其他线程(时刻监听是否有延时任务、用户点击、微任务)
1. setTimeout(() => { console.log('B'); Promise.resolve().then(() => { console.log('C'); });}, 0); 命名为 S1 推到延时队列中等待执行输出结果:ADJFIGEBCH