title: 事件循环机制
date: 2025-09-28 16:51:21
tags: vue
一、什么是事件循环机制
事件循环机制(Event Loop)是 JavaScript 在运行时处理 同步任务 和 异步任务 的核心机制。它的存在是因为 JS 是单线程语言 —— 同一时间只能执行一段代码,但又需要同时处理异步操作(比如定时器、网络请求、DOM 事件等)。
二、事件循环机制运作
1. 执行模型
JavaScript 的运行时会维护以下几个关键部分:
- 调用栈(Call Stack)
存放正在执行的代码(函数调用)。同步代码会一条一条执行,先入后出。 - 任务队列(Task Queue / Callback Queue)
异步任务的回调会被放到任务队列里,等调用栈清空后再取出执行。
队列分为两种:- 宏任务队列(Macro Task Queue)
例如:setTimeout、setInterval、setImmediate、I/O、UI 渲染。 - 微任务队列(Micro Task Queue)
例如:Promise.then、process.nextTick、MutationObserver。
- 宏任务队列(Macro Task Queue)
- 事件循环(Event Loop)
不断检查 调用栈是否为空,如果为空,就按顺序取任务队列里的任务放进调用栈执行。
执行顺序是:- 先执行当前调用栈里的同步代码。
- 再执行所有的微任务队列。
- 最后执行一个宏任务,然后再次循环。
2. 流程图(简化)
同步代码 → 执行
↓
调用栈清空?
↓ 是
先执行所有微任务
↓
取一个宏任务执行
↓
重复循环...
3. 例子
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
Promise.resolve().then(() => {
console.log("C");
});
console.log("D");
执行过程:
console.log("A")→ AsetTimeout回调放入 宏任务队列Promise.then回调放入 微任务队列console.log("D")→ D- 栈清空 → 执行所有微任务 → C
- 执行下一个宏任务 → B
输出结果:
A
D
C
B
👉 总结一句话:
事件循环机制就是“同步先执行,异步分队列,先清空微任务,再取宏任务,循环往复”。
三、关于async和await
在 事件循环机制 里,async/await 其实是对 Promise 的语法糖,本质上还是基于 微任务(microtask) 来调度的。
来拆开解释:
-
async函数async函数会返回一个Promise对象。- 函数体内部如果抛出异常,相当于返回一个 reject 的 Promise。
async function test() { return 42; } test().then(console.log); // 42等价于:
function test() { return Promise.resolve(42); }
-
await表达式await会暂停async函数后续代码的执行,把await右侧的表达式(无论是不是 Promise)包装成一个 Promise。- 然后把后续的执行逻辑放进 微任务队列(microtask queue),等待该 Promise resolve 后再继续。
举个例子:
async function foo() { console.log(1); await Promise.resolve(); // 挂起,后续逻辑放到微任务里 console.log(2); } foo(); console.log(3);执行顺序是:
1 3 2因为
await后面的逻辑(console.log(2)) 被放到了 微任务队列。
总结:
async函数本身返回 Promise。await会 挂起 async 内部代码,把后续代码放到 微任务队列。- 所以
async/await的本质:Promise + 微任务。
四、复杂事件循环机制
有的 👍
在面试里,如果你想考察候选人对 事件循环 + async/await + Promise + 宏任务/微任务 的深入理解,可以设计一些更复杂的场景。下面我给你几个递进难度的例子,每个都带执行顺序解析。
📌 场景 1:async/await 和 Promise.then
async function foo() {
console.log("A");
await Promise.resolve();
console.log("B");
}
foo();
Promise.resolve().then(() => console.log("C"));
console.log("D");
执行顺序:
A
D
C
B
👉 解析:
foo()执行到await前打印 “A”。await和Promise.then都是 微任务,但then的注册先于await内部的恢复逻辑。- 所以 “C” 先于 “B”。
📌 场景 2:多个 await + 宏任务
async function foo() {
console.log(1);
await Promise.resolve();
console.log(2);
await new Promise(resolve => setTimeout(resolve, 0));
console.log(3);
}
foo();
console.log(4);
执行顺序:
1
4
2
3
👉 解析:
- “1” 同步执行。
- “4” 也属于同步任务,所以跟在后面。
- 第一个
await Promise.resolve()→ 微任务,输出 “2”。 - 第二个
await setTimeout→ 宏任务,需要等下一轮事件循环,所以 “3” 最后。
📌 场景 3:多个 async 嵌套
async function foo() {
console.log("foo start");
await bar();
console.log("foo end");
}
async function bar() {
console.log("bar start");
await Promise.resolve();
console.log("bar end");
}
foo();
console.log("script end");
执行顺序:
foo start
bar start
script end
bar end
foo end
👉 解析:
foo()调用时打印 “foo start”。- 调用
bar(),打印 “bar start”。 bar()内遇到await→ 把后续逻辑放到微任务。- 同步代码执行完,打印 “script end”。
- 微任务队列:先执行
bar end,再继续执行foo end。
📌 场景 4:综合大考(Promise + async/await + setTimeout)
console.log(1);
setTimeout(() => console.log(2), 0);
async function test() {
console.log(3);
await Promise.resolve();
console.log(4);
}
test();
Promise.resolve().then(() => console.log(5));
console.log(6);
执行顺序:
1
3
6
5
4
2
👉 解析:
1、3、6:同步任务。await和Promise.then→ 都是微任务,先注册的 “5”,再执行 “4”。setTimeout→ 宏任务,最后执行 “2”。
✅ 这些场景能很好地考察候选人对 事件循环执行顺序 的理解。
五、总结注意事项
1.acync和await属于微任务,执行再同步任务后面,宏任务前面
2.acync和await会将他后面的代码一起带入到微任务,相当于把后面的代码放到promise的.then当中
3.acync和await在多层嵌套使用的使用,在执行完最后一个同步任务后,会先执行最后一个被纳入到微任务的代码,和栈的后进先出逻辑一样
4.Promise 构造函数里的代码 → 同步任务,Promise 的回调(then/catch/finally) → 微任务

5.可以把acync和await后面的内容看成promise.then当中的内容,而acync和await中调用的方法则可以看成promise 当中构造函数里的代码
async function inner() {
console.log("inner start");
await Promise.resolve();
console.log("inner end");
}
async function outer() {
console.log("outer start");
await inner();
console.log("outer end");
}
outer();
console.log("script end");
可以看成promsie写法
function inner() {
console.log("inner start");
return Promise.resolve().then(() => {
console.log("inner end");
});
}
function outer() {
console.log("outer start");
return inner().then(() => {
console.log("outer end");
});
}
outer();
console.log("script end");
输出
outer start
inner start
script end
inner end
outer end

880

被折叠的 条评论
为什么被折叠?



