事件循环的执行顺序

事件循环机制的具体流程可以拆解为 “主线程执行栈”“任务队列”“循环调度” 三个核心环节的协作,以下是详细步骤:

一、初始化:区分任务类型

  1. 同步任务:进入主线程的 “执行栈”,按顺序立即执行(如变量声明、函数调用、条件判断等)。
  2. 异步任务:不进入执行栈,而是被 “宿主环境”(浏览器 / Node.js)接管,等待触发条件(如定时器到期、网络请求完成、DOM 事件触发)。
    触发后,异步任务的回调函数会被放入对应的任务队列(分为宏任务队列和微任务队列)。

二、核心循环流程

事件循环按固定规则重复以下步骤,直到所有任务执行完毕:

步骤 1:清空执行栈

  • 主线程优先执行执行栈中的所有同步任务,直到执行栈为空(即当前同步代码全部执行完毕)。

步骤 2:处理微任务队列

  • 检查微任务队列,如果有任务:
    • 按顺序将微任务从队列中取出,放入执行栈执行,直到微任务队列清空(即使执行过程中新增了微任务,也会在本轮一并执行)。
  • 微任务类型包括:Promise.then/catch/finallyasync/await(本质是 Promise 的语法糖)、queueMicrotask()、Node.js 中的process.nextTick(优先级高于其他微任务)。

步骤 3:处理 UI 渲染(浏览器环境特有)

  • 微任务队列清空后,浏览器会触发一次 UI 渲染(如 DOM 更新、样式计算等),Node.js 环境无此步骤。

步骤 4:处理宏任务队列

  • 宏任务队列中取出一个任务(按入队顺序,先进先出),放入执行栈执行。
  • 执行完毕后,该宏任务的本轮处理结束。

步骤 5:重复循环

  • 回到步骤 1,再次检查执行栈是否为空,重复上述流程(清空执行栈→处理微任务→UI 渲染→处理一个宏任务)。

三、关键规则总结

  1. 微任务优先于宏任务:每处理完一个宏任务后,必须先清空所有微任务,再处理下一个宏任务。
  2. 宏任务一次处理一个:即使宏任务队列中有多个任务,每次循环只取一个执行,避免长时间阻塞主线程。
  3. 微任务即时处理:微任务队列中的任务会被一次性全部执行(包括执行过程中新增的微任务),确保异步操作的 “即时性”(如 Promise 的回调需要尽快执行)。

四、示例:直观理解流程

以下代码的执行顺序可清晰体现事件循环流程:

 

javascript

// 同步任务1
console.log('同步任务1');

// 宏任务:setTimeout
setTimeout(() => {
  console.log('宏任务1(setTimeout)');
  // 宏任务中新增的微任务
  Promise.resolve().then(() => console.log('宏任务1中的微任务'));
}, 0);

// 微任务:Promise.then
Promise.resolve().then(() => {
  console.log('微任务1(Promise)');
  // 微任务中新增的微任务
  Promise.resolve().then(() => console.log('微任务2(嵌套)'));
  // 微任务中新增的宏任务
  setTimeout(() => console.log('微任务中新增的宏任务'), 0);
});

// 同步任务2
console.log('同步任务2');

执行顺序解析

  1. 执行同步任务:同步任务1 → 同步任务2(执行栈清空)。
  2. 处理微任务队列:
    • 执行第一个微任务:微任务1(Promise)
    • 执行过程中新增的微任务入队,继续执行:微任务2(嵌套)(微任务队列清空)。
  3. 浏览器 UI 渲染(假设此时有 DOM 更新)。
  4. 处理宏任务队列中的第一个任务:宏任务1(setTimeout)
    • 执行过程中新增的微任务入队,执行栈清空后,处理该微任务:宏任务1中的微任务
  5. 再次循环,处理宏任务队列中新增的任务:微任务中新增的宏任务

最终输出顺序

plaintext

同步任务1
同步任务2
微任务1(Promise)
微任务2(嵌套)
宏任务1(setTimeout)
宏任务1中的微任务
微任务中新增的宏任务

总结

事件循环的核心逻辑是:“同步优先,微任务次之,宏任务最后,循环往复”。通过这种机制,单线程的 JavaScript 既能保证同步任务的有序执行,又能高效处理异步操作,避免因等待而阻塞,实现 “非阻塞” 的并发效果。

 

在事件循环(Event Loop)过程中,对于执行微任务或宏任务时新进来的同步任务、微任务的处理方式如下:
 
执行微任务时新任务的处理
 
1. 同步任务:如果在执行微任务的过程中遇到同步任务,同步任务会立即在当前调用栈中执行。因为JavaScript是单线程的,调用栈在同一时间只能执行一个任务,所以新的同步任务会打断微任务的执行流程,优先执行完同步任务后,才会继续执行微任务剩余部分。例如:
 
Promise.resolve().then(() => {
    console.log('微任务开始');
    // 同步任务
    for (let i = 0; i < 1000; i++) {
        console.log(i);
    }
    console.log('微任务结束');
});
 
 
在这个例子中, for  循环这个同步任务会在微任务的回调函数中立即执行,执行完后才会输出  微任务结束 。
2. 微任务:在执行微任务过程中产生的新微任务,会被添加到微任务队列的末尾。当当前微任务执行完毕(包括执行过程中遇到的同步任务执行完毕),事件循环会继续从微任务队列中取出下一个微任务执行,直到微任务队列为空。例如:
 
Promise.resolve().then(() => {
    console.log('第一个微任务开始');
    Promise.resolve().then(() => {
        console.log('新产生的微任务');
    });
    console.log('第一个微任务结束');
});
 
 
输出顺序为: 第一个微任务开始 , 第一个微任务结束 , 新产生的微任务 。因为新产生的微任务被添加到微任务队列末尾,在第一个微任务执行完毕后才会执行。
 
执行宏任务时新任务的处理
 
1. 同步任务:在执行宏任务过程中遇到同步任务,同样会立即在当前调用栈中执行。同步任务会按照代码顺序依次执行,直到宏任务中的同步代码执行完毕。例如:
 
setTimeout(() => {
    console.log('宏任务开始');
    // 同步任务
    for (let i = 0; i < 1000; i++) {
        console.log(i);
    }
    console.log('宏任务结束');
}, 0);
 
 
这里的  for  循环同步任务会在  setTimeout  的回调函数(宏任务)中立即执行,执行完后输出  宏任务结束 。
2. 微任务:宏任务执行过程中产生的微任务,会被添加到微任务队列的末尾。当宏任务执行完毕(包括宏任务中的同步任务执行完毕),调用栈为空时,事件循环会开始处理微任务队列,依次执行微任务队列中的所有微任务,直到微任务队列为空,然后才会从宏任务队列中取出下一个宏任务执行。例如:
 
setTimeout(() => {
    console.log('宏任务开始');
    Promise.resolve().then(() => {
        console.log('宏任务中产生的微任务');
    });
    console.log('宏任务结束');
}, 0);
 
 
输出顺序为: 宏任务开始 , 宏任务结束 , 宏任务中产生的微任务 。因为宏任务中产生的微任务在宏任务执行完毕后,才会在处理微任务队列时被执行。
 
综上所述,在事件循环中,无论是宏任务还是微任务执行过程中产生的新任务,都会遵循相应规则进行处理,确保JavaScript单线程环境下任务的有序执行。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Komorebi_9999

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值