Skip to content

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") // 执行完毕-1
console.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(等待执行) - 执行完毕-3
Step5(等待执行) - 执行完毕-4
Step7(等待执行) - 执行完毕-5
Step4(等待执行) - 执行完毕-6

用户交互队列(优先级高)

延时队列(优先级最低)

Step2(等待执行) - 执行完毕-7
Step6(等待执行) - 执行完毕-8

其他线程(时刻监听是否有延时任务、用户点击、微任务)

Step2 0 秒后到达,将该任务添加到延时队列中等待执行
Step3 将该任务添加到微队列中等待执行
Step5 会创建一个微任务,添加到微队列
Step6 添加到延时队列
Step7 将该任务添加到微队列中等待执行
执行结果:
Step 1
Step 8
Step 3
Step 5
Step 7
Step 4
Step 2
Step 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
执行结果:
A
J
D
G
H
F
B
C
I
E

代码示例-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 推到延时队列中等待执行
输出结果:
A
D
J
F
I
G
E
B
C
H

什么是异步