news 2026/10/1 12:37:11

《一文搞懂 JavaScript 事件循环(Event Loop):宏任务、微任务与 async/await》

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《一文搞懂 JavaScript 事件循环(Event Loop):宏任务、微任务与 async/await》

文章目录

  • JavaScript 事件循环(Event Loop):宏任务、微任务与 async/await
  • 1.预备知识
    • 1.1 同步和异步
  • 2.事件循环基础知识
    • 2.1 事件循环的组成-执行栈和任务队列
    • 2.2 事件循环(Event loop)的过程
    • 2.3 宏观任务 微观任务
  • 3.实战题目
    • 3.1 分析含有Promise的实战题目
    • 3.2 题目实战

JavaScript 事件循环(Event Loop):宏任务、微任务与 async/await

1.预备知识

1.1 同步和异步

在讨论事件循环前,首先需要先了解同步与异步的概念

JavaScript 是单线程语言,一行代码执行完才会再执行下一行,这个概念称之为同步。

但是如果取一个服务器中拿数据,需要等待 10 秒才能拿到,等待途中无法进行任何操作,是很糟糕的使用体验,于是就有了异步。

异步的程式或事件,并不会阻碍主线程执行其他代码,例如,拿取资料当作是一个异步事件,异步事件会在完成之后再通知主线程,而在这之中,主线程可以继续执行其他代码、使用者互动也不受异步事件的阻挡。而浏览器或其他的执行环境(例如Node.js) 之所以能够实践异步,正是因为有**事件循环(Event loop)**的机制。透过事件循环机制,能有效解决JavaScript 单执行绪的问题,让耗时的操作不会阻塞主线程。

2.事件循环基础知识

2.1 事件循环的组成-执行栈和任务队列

事件循环不存在JavaScript 本身,而是由JavaScript 的执行环境(浏览器或Node.js) 来实现的,其中包含几个概念:

  • 堆(Heap):堆是一种数据结构,拿来储存物件
  • 栈(Stack):采用后进先出的规则,当函数执行时,会被添加到栈的顶部,当执行完成时,就会从顶部移出,直到栈被清空
  • 队列(Queue):也是一种数据结构,特性是先进先出(FIFO)。在JavaScript 的执行环境中,等待处理的任务会被放在队列(Queue) 里面,等待栈(Stack) 被清空时,会从队列(Queue)中拿取第一个任务进行处理
  • 事件循环(Event loop):事件循环会不断地去查看栈(Stack) 是否空出,如果空出就会把队列(Queue)中等待的任务放进栈(Stack)中执行

2.2 事件循环(Event loop)的过程

1.User Interface(橙色部分)的User Interface,是浏览器页面,用户操作的来源,比如点击按钮,输入,页面展示等等

UI 不执行 JS,只是触发事件。

2.JavaScript Runtime(黄色部分),其中有 Heap(堆)和 Call Stack(调用栈)

Heap 负责,存数据,对象/数组/函数对象,不关心执行顺序,Heap=内存仓库。

Call Stack(调用栈),执行 JS 代码,函数一层一层压栈,单线程,只能有一个。

3.Web APIs(绿色部分)

理解事件循环的关键

这里的东西不是 JS 在跑,而是:浏览器提供的能力,独立于 JS 线程。

包括:

  • DOM 事件(click)
  • AJAX/fetch
  • 定时器(setTimeout)

JS 调用它们,只是登记一下

4.Callback Queue(蓝色部分)

回调的候车室,分成了宏任务队列(Task Queue) 微任务队列(Microtask Queue)

中间的循环箭头,是一种机制,不停的检查:call Stack 是否为空,空了就从 Queue 拿一个回调,不空就等着

完整执行流程的描述:

Step 1:

UI 触发 JS,同步代码直接执行

Step 2:

遇到 Web API(异步任务)(如 setTimeout)

1.setTimeout() 进 Call Stack

2.浏览器 Web API 接管计时

3.JS 立刻继续往下走(不等)

setTimeout(fn, 1000)

setTimeout() 本身是一次“同步函数调用”,所以一定会先进 Call Stack;但 setTimeout 里传的 callback,才会进入宏任务队列。

部分是什么去哪
setTimeout()同步函数调用Call Stack
fn(回调)异步回调宏任务队列(Task Queue)

Step 3:Web API 完成 → 回调进 Queue

  • 浏览器说:「callback 已经准备好了」
  • 放进 Callback Queue,不是立刻执行

Step 4:Event Loop 把回调塞回 Call Stack

条件只有一个:Call Stack 必须是空的

满足后:

  • callback → Call Stack
  • JS 开始执行回调函数

2.3 宏观任务 微观任务

JavaScript 中的异步任务又分成宏任务(Macro Task) 和微任务(Micro Task),这两者的执行顺序是不同的

常见的宏任务与微任务如下:

  • 宏任务:script(整体程式码)、setTimeout、setInterval、I/O、事件、postMessage、MessageChannel、setImmediate(Node.js)
  • 微任务:Promise.then、Promise.catch、MutaionObserver、process.nextTick(Node.js)。

async 函数本身:同步调用,await 的本质:Promise.then 的语法糖.

比如 async1 中有一个await async2()

await async2() 会同步立即执行 async2,并且把 await 后面的代码包装成一个「微任务」去执行。

执行顺序如下:

  • 执行一次宏任务(最开始会是整个srcipt所以上面的例子会先执行console.log(1))
  • 执行过程中如果遇到宏任务,就放进宏任务列队
  • 执行过程中如果遇到微任务,就放进微任务列队
  • 当执行栈空了,先检查微任务列队,如果有微任务,就依序执行直到微任务列队为空
  • 接着进行浏览器的渲染,渲然完后开始下一个宏任务(回到最开始的步骤)

3.实战题目

3.1 分析含有Promise的实战题目

Promise 和 setTimeout 都是同步注册异步任务的机制,只是 Promise 注册的是微任务,setTimeout 注册的是宏任务。

Promised 中的执行器函数是同步立即执行,.then .catch 才是异步的微任务

console.log('A');constp=newPromise((resolve,reject)=>{// 这段就是 executor(执行器函数)console.log('B: executor start');resolve('OK');console.log('C: executor end');});p.then((value)=>{console.log('E: then',value);});console.log('D');

resolve 会标记 Promise 为 fulfilled,并在「当前执行上下文结束后」派发微任务,将.then 加入到微任务队列中

「当前执行上下文结束后」指的是整个当前宏任务(script)执行完毕,也就是 call Stack 为空。

输出如下:

A B: executor start C: executor end' D

此时call Stack 栈空,将微任务队列加入 call Stack,保证call Stack 栈空后 1,微任务队列也为空,才能执行下一个宏任务

最终如下:

A B: executor start C: executor end' D E: then

promise 中的 resolve

.then 只有在 Promise「状态发生改变(resolve / reject)」的那一刻,才会被“派发”为微任务

Promise 的状态变化是同步的、确定的、不可回退的

resolve 在当前调用栈里已经执行了吗?

  • 执行了 → Promise 已 fulfilled → then 进微任务
  • 没执行 → Promise 还 pending → then 只注册

情况一: promise 立刻变成fulfilled

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

这个 promise 立刻变成fulfilled

情况二:执行到 resolve,promise 状态变成fulfilled

setTimeout(function(){console.log("setTimeout 2");resolve("resolve 1");},0);}).then((res)=>{

这个就得等着resolve 执行才能变成fulfilled

情况三:不可预测

fetch('/api').then(res => { console.log('then'); });

Promise 的 resolve 时机是由外部事件决定的(如网络、I/O、定时器),

对静态代码分析来说是不可预测的,但对运行时来说是确定的

所以面试题一般都是明确告知 resolve

3.2 题目实战

题目一:

console.log(1);setTimeout(function(){console.log(2);},0);Promise.resolve().then(function(){console.log(3);}).then(function(){console.log(4);});

输出:

1; 3; 4; 2;

题目二:

console.log("begins");setTimeout(()=>{//1console.log("setTimeout 1");Promise.resolve().then(()=>{//2console.log("promise 1");});},0);newPromise(function(resolve,reject){// 3console.log("promise 2");setTimeout(function(){// 4console.log("setTimeout 2");resolve("resolve 1");},0);}).then((res)=>{//5console.log("dot then 1");setTimeout(()=>{//6console.log(res);},0);});

宏任务队列:script(整体程式码)

微任务队列:无

宏任务队列:(1) (4)

微任务队列:无

此时微任务队列为空应该继续执行宏任务(1)

宏任务队列:(4)

微任务队列:(2)

此时宏任务(1)执行完应该执行微任务(2)

此时应该继续执行加入新的宏任务

宏任务队列:(4)

微任务队列:

执行到这 resolve(“resolve 1”); (5)加入微任务队列

宏任务队列:

微任务队列:(5)

宏任务队列:(6)

微任务队列:

输出:

begins; promise 2; setTimeout 1; promise 1; setTimeout 2; dot then 1; resolve 1;

题目三:

asyncfunctionasync1(){console.log("async1 start");awaitasync2();console.log("async1 end");}asyncfunctionasync2(){console.log("async2");}console.log("script start");setTimeout(function(){console.log("setTimeout");},0);async1();newPromise(function(resolve){console.log("promise1");resolve();}).then(function(){console.log("promise2");});console.log("script end");

输出如下:

script start async1 start async2 promise1 script end async1 end promise2 setTimeout

注意:注意,await后的程式码会被放到微任务列队,所以不会马上印出'async1 end'而是会把它放到微任务列队

过程:

  • 执行代码
  • setTimeout 进入宏任务队列
  • 执行async1,然后呼叫await async2()所以印出’async2’。但是 async2 是同步立即执行的。注意,await`后的程式码会被放到微任务列队,所以不会马上印出’async1 end而是会把它放到微任务列队
  • 执行 promise 的执行器函数,promise 状态变成fullfiled,等待等待 call Stack 为空的时候将.then 加入微任务队列
  • 将两个微任务队列加入 call Stack,执行两个微任务有先后顺序
  • call Stack 为空,微任务队列为空,从宏任务队列中拿出一个宏任务加入 call Stack
  • 执行 call Stack
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 17:33:38

python基于Python对b站热门视频的数据分析与研究

学校代码 13651 编 号 系统简介 随着互联网技术不断地发展,网络与大数据成为了人们生活的一部分,而对B站热门视频的数据分析与研究作为网上应用的一个全新的体现,由于其特有的便捷性,已经被人们所接受。目前主流的对B站热门视频…

作者头像 李华
网站建设 2026/9/30 7:13:20

【财务专业论文写作模版】上海XXXX科技有限公司财务报表分析

摘 要 随着社会物质标准的不断提高,互联网技术也在迅速发展。为了满足人们日常生活中对娱乐和学习的需求,视频作为一种重要工具已然崭露头角,成为我们不可或缺的伙伴。从教育到文化,从消遣到娱乐,在各个领域都可以看到…

作者头像 李华
网站建设 2026/9/29 9:22:58

Open-AutoGLM宣传视频下载难题全解析,专家亲授4种有效方法

第一章:智谱Open-AutoGLM 宣传视频下载现状与挑战随着人工智能技术的普及,智谱AI推出的Open-AutoGLM项目受到广泛关注。其宣传视频作为核心传播载体,在多个平台上线后引发了开发者社区的高度兴趣。然而,目前该视频尚未提供官方直接…

作者头像 李华
网站建设 2026/9/30 15:36:10

2025年AI市场舆情分析榜单,原圈科技为何能一马当先?

在众多AI市场舆情分析工具中,原圈科技的精准推理引擎被普遍视为2025年的优先推荐对象。其突出之处在于,它不仅能执行深度舆情洞察,更能通过融合企业内外数据,将分析结果无缝转化为可执行的商业行动,在技术能力与行业适…

作者头像 李华
网站建设 2026/9/29 9:22:58

为什么顶级智能设备都在用Open-AutoGLM做语音唤醒?真相曝光

第一章:Open-AutoGLM结合语音识别实现语音唤醒在智能设备日益普及的今天,语音唤醒技术成为人机交互的关键入口。Open-AutoGLM 作为一款支持多模态理解与生成的开源大模型框架,具备强大的自然语言理解能力,结合实时语音识别模块&am…

作者头像 李华
网站建设 2026/9/29 9:22:58

网络安全入门学习路线图:零基础自学从这开始

由于我之前写了不少网络安全技术相关的故事文章,不少读者朋友知道我是从事网络安全相关的工作,于是经常有人在微信里问我: 我刚入门网络安全,该怎么学?要学哪些东西?有哪些方向?怎么选&#xf…

作者头像 李华