javaScript事件循环机制


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)
      例如:setTimeoutsetIntervalsetImmediateI/OUI 渲染
    • 微任务队列(Micro Task Queue)
      例如:Promise.thenprocess.nextTickMutationObserver
  • 事件循环(Event Loop)
    不断检查 调用栈是否为空,如果为空,就按顺序取任务队列里的任务放进调用栈执行。
    执行顺序是:
    1. 先执行当前调用栈里的同步代码。
    2. 再执行所有的微任务队列。
    3. 最后执行一个宏任务,然后再次循环。

2. 流程图(简化)

同步代码 → 执行
          ↓
调用栈清空?
          ↓ 是
先执行所有微任务
          ↓
取一个宏任务执行
          ↓
重复循环...

3. 例子

console.log("A");

setTimeout(() => {
  console.log("B");
}, 0);

Promise.resolve().then(() => {
  console.log("C");
});

console.log("D");

执行过程:

  1. console.log("A")A
  2. setTimeout 回调放入 宏任务队列
  3. Promise.then 回调放入 微任务队列
  4. console.log("D")D
  5. 栈清空 → 执行所有微任务 → C
  6. 执行下一个宏任务 → B

输出结果:

A
D
C
B

👉 总结一句话:
事件循环机制就是“同步先执行,异步分队列,先清空微任务,再取宏任务,循环往复”。

三、关于async和await

事件循环机制 里,async/await 其实是对 Promise 的语法糖,本质上还是基于 微任务(microtask) 来调度的。

来拆开解释:

  1. async 函数

    • async 函数会返回一个 Promise 对象。
    • 函数体内部如果抛出异常,相当于返回一个 reject 的 Promise
    async function test() {
      return 42;
    }
    test().then(console.log); // 42
    

    等价于:

    function test() {
      return Promise.resolve(42);
    }
    

  1. 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/awaitPromise.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”。
  • awaitPromise.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

👉 解析:

  • 136:同步任务。
  • awaitPromise.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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值