解密 Event Loop:宏任务与微任务的奇妙旋律

前言

JavaScript是一门单线程的编程语言,这一特性决定了在任何给定时刻,JavaScript程序只能执行一个任务。然而,为了使JavaScript能够处理异步操作而不阻塞主线程,引入了Event Loop(事件循环)的机制。本文将深入探讨JavaScript的Event Loop,解释它的工作原理,并通过案例帮助新手更好地理解这一关键概念。

在学习Event Loop之前,我们先给大家科普些小知识

什么是单线程?

单线程是指在任何给定时刻,程序或进程只能执行一个任务或代码块。在单线程模型中,代码是按照顺序逐行执行的,每个操作都要等待前一个操作的完成。

什么是宏任务?

宏任务代表的是那些较大粒度的任务,通常包括I/O 操作、定时器事件(setTimeoutsetInterval)、用户交互事件(点击输入)等。每个宏任务都会在一个事件循环中执行,宏任务之间会按照一定的顺序排队执行。

js 复制代码
// 宏任务
let a = 2
console.log(a);

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

let b = 3
console.log(b);

在上面的例子中,整体代码执行是同步的,虽然setTimeout设置了超时时间为0毫秒,但它仍然会被放入宏任务队列,而不是立即执行。这是因为setTimeout的第二个参数表示最少等待的时间,而非确切的时间。所以这个回调函数会在当前宏任务执行完毕后,被放入宏任务队列等待执行。

什么是微任务?

微任务是相对于宏任务更小粒度的任务,典型的微任务包括Promise的回调函数、process.nextTick等。微任务的执行时机是在当前宏任务执行完毕、下一个宏任务开始之前。

js 复制代码
console.log('start');
new Promise((resolve, reject) => {
  console.log('123');
  resolve();
})
// 微任务
.then(() => {
  console.log('then');
})
console.log('end');

在上面的例子中,Promise的构造函数是同步执行的。因此,这里会打印123到控制台。同时,通过resolve方法将 Promise 的状态设置为resolved。然后在then方法中注册了一个回调函数,这个回调函数会在微任务队列中等待执行。由于Promise的状态是同步设置为resolved的,所以这个微任务会立即执行。

宏任务与微任务执行顺序与优先级

在一个宏任务中,当所有同步代码和宏任务执行完毕后,会检查微任务队列,依次执行其中的微任务。然后,选择下一个宏任务执行,这个过程不断循环。

js 复制代码
console.log("Start");

setTimeout(() => {
  console.log("这是宏任务");
}, 0);

Promise.resolve().then(() => {
  console.log("这是微任务");
});

console.log("End");

在上述例子中,微任务的执行优先于下一个宏任务的开始,即使微任务是在当前宏任务中产生的。

什么是Event Loop?

Event Loop是JavaScript处理异步操作的核心机制。它使得JavaScript在执行同步任务的同时,能够异步执行一些任务,而不会阻塞主线程。这种机制通过一系列的步骤来实现,主要包括执行同步代码、执行异步代码、检查消息队列、执行消息队列中的任务等。

Event Loop的基本工作流程

1. 执行同步代码(这属于宏任务)

JS引擎首先会执行当前执行栈中的同步代码,这是按照代码的顺序逐行执行的部分。

2. 当执行栈为空,查询是否有异步代码需要执行

当执行栈为空时,JS 引擎会去查询是否有异步代码需要执行。这可以是注册的回调函数、定时器到期、事件触发等。

3. 执行微任务

如果有微任务(Promise 的回调、process.nextTick等),它们会在当前宏任务执行完毕后立即执行。微任务的执行时机优先于下一个宏任务开始。

4. 如果有需要,会渲染页面

在一些环境中,比如浏览器中,可能在这个时候进行页面的渲染。这确保了用户界面的及时更新。

5. 执行宏任务

如果有宏任务(定时器事件、I/O 操作、用户交互事件),它们会被放入宏任务队列中,等待下一轮 Event Loop 执行。

总结

整个过程是一个循环,不断地执行同步代码、查询异步任务、执行微任务、渲染页面以及执行宏任务。这个循环一直持续,形成了 JavaScript 异步执行的基本模型。

相关推荐
大家的林语冰18 分钟前
👍 超越 ESLint,Oxc 优先采用 TypeScript 7,Rust 和 Go 梦幻联动!
前端·javascript·typescript
樊小肆2 小时前
# 你还在等DeepSeek官方 agent Harness‌? 来试试 DeepSeeker-Code吧
前端·人工智能·后端
樊小肆2 小时前
2568 万 token 才花 2 块 2:聊聊 DeepSeeker-Code 怎么吃满上下文缓存
前端·人工智能·后端
众人皆醒我独醉2 小时前
大模型训练优化:FSDP、DeepSpeed ZeRO 与混合精度
后端·面试·gpu
JarvanMo2 小时前
Flutter 3.47: material/cupertino终于解耦了
前端
郭邯2 小时前
一次「字符洁癖」引发的思考:我用 AI 写了个全角半角转换工具
前端
想要成为糕糕手2 小时前
🚀 在浏览器里跑 DeepSeek-R1?WebGPU 端侧推理实战(五)—— 中断、重置、缓存与流式生成
前端·react.js·llm
GuWenyue2 小时前
后端接口又双叒没写好?3 步搭建前端 Mock,从此告别"傻等后端"
前端·mocha
海兰2 小时前
【数据采集】开源的 Web 数据采集与处理Firecrawl(一)
前端·开源
vivo互联网技术2 小时前
从一键检测到 AI 修复:我们如何把无障碍检查做进研发流程
前端·人工智能