---
title: 一道题彻底理清 Browser Context 的 Event Loop
description: 众所周知，JavaScript 是一门单线程语言，同一时刻只能执行一个任务。同步任务都在主线程上执行，形成一个执行栈。遇到异步任务，如果未拿到结果一直等待的话，就会阻塞后续代码执行，这种结果是我们不想看到的。而Event Loop 事件循环就是解决异步阻塞的一种机制。
image: https://blog.advance.ai/hubfs/shutterstock_110678570.jpg
---

[![wordmark-green-01-1](https://blog.advance.ai/hs-fs/hubfs/wordmark-green-01-1.png?width=446&height=54&name=wordmark-green-01-1.png "wordmark-green-01-1")](https://www.advanceai.com.cn/)

- 核心产品 
    - - AI 生物识别验证 
                  - [活体检测](https://www.advanceai.com.cn/liveness-detection)
                  - [人脸图像比对](https://www.advanceai.com.cn/face-comparison)
                  - [人脸搜索](https://www.advanceai.com.cn/face-search)
                  - [多维依据凭证人脸信用评分](https://www.advanceai.com.cn/facial-multi-loan-score)
          - AI 文档验证 
                  - [证件识别](https://www.advanceai.com.cn/id-document-recognition)
- 解决方案 
    - [数字身份验证](https://www.advanceai.com.cn/digital-identity-verification)
    - [视频身份验证](https://www.advanceai.com.cn/video-identification)
    - [更多部署方案](https://www.advanceai.com.cn/deployment-options)
- 行业应用 
    - [金融服务](https://www.advanceai.com.cn/financial-services)
    - [银行业](https://www.advanceai.com.cn/banking)
    - [旅游出行](https://www.advanceai.com.cn/travel)
    - [共享经济](https://www.advanceai.com.cn/sharing-economy)
    - [电子商务](https://www.advanceai.com.cn/e-commerce)
    - [金融科技](https://www.advanceai.com.cn/fintech)
- ADVANCE.AI研究 
    - [市场活动](https://www.advanceai.com.cn/events)
    - [白皮书和报告](https://www.advanceai.com.cn/whitepaper-reports_1)
    - [博客中心](https://www.advanceai.com.cn/blog)
- 关于我们 
    - [公司简介](https://www.advanceai.com.cn/about-advance-ai)
    - [安全团队](https://www.advanceai.com.cn/security-team)
    - [合作伙伴](https://www.advanceai.com.cn/partnership)
    - [新闻动态](https://www.advanceai.com.cn/company-news)
    - [加入我们](https://www.advanceai.com.cn/join-us)

- 核心产品 
    - 人工智能面部识别 
          - [活体检测](https://www.advance.ai/cn/liveness-detection)
          - [人脸图像比对](https://www.advance.ai/cn/face-comparison)
          - [人脸搜索](https://www.advance.ai/cn/face-search)
          - [欺诈人脸](https://www.advance.ai/cn/fraud-face)
          - [多维数据人脸信用评分](https://www.advance.ai/cn/facial-multi-loan-score)
    - 人工智能文件识别 
          - [证件识别](https://www.advance.ai/cn/id-document-recognition)
          - [身份证打码工具](https://www.advance.ai/cn/id-card-masking)
          - [证件打码](http://demo2019.test.yongsy.net/zhixinyin/index.html)
    - 人工智能文件处理 
          - [收据识别](https://www.advance.ai/cn/receipt-recognition)
          - [银行流水账单分析工具](https://www.advance.ai/cn/bank-statement-analyser)
    - 智慧零售货架识别 
          - [智慧零售](https://www.advance.ai/cn/smart-retailing)
- 解决方案 
    - [数字身份验证](https://www.advance.ai/cn/digital-identity-verification)
    - [视频身份验证](https://www.advance.ai/cn/video-identification)
    - [联合建模](https://www.advance.ai/cn/joint-modelling)
    - [更多部署方案](https://www.advance.ai/cn/deployment-options)
- 行业应用 
    - [金融服务](https://www.advance.ai/cn/financial-services)
    - [银行业](https://www.advance.ai/cn/banking)
    - [数字支付](https://www.advance.ai/cn/payment)
    - [医疗保健](https://www.advance.ai/cn/healthcare)
    - [旅游出行](https://www.advance.ai/cn/travel)
    - [共享经济](https://www.advance.ai/cn/sharing-economy)
    - [电子商务](https://www.advance.ai/cn/e-commerce)
    - [金融科技](https://www.advance.ai/cn/fintech)
    - [智慧物联](https://www.advance.ai/cn/internet-of-things)
- 领创研究 
    - [分享会](https://www.advance.ai/cn/events)
- 关于我们 
    - [公司简介](https://www.advance.ai/cn/about-advance-ai)
    - [安全团队](https://www.advance.ai/cn/security-team)
    - [新闻动态](https://www.advance.ai/cn/company-news)
    - [加入我们](https://www.advance.ai/cn/join-us)
    - [联系我们](https://www.advance.ai/cn/contact-us)

免费试用

中

- [EN](https://advance.ai/)

![](https://blog.advance.ai/hubfs/shutterstock_110678570.jpg)

# 一道题彻底理清 Browser Context 的 Event Loop

 Published by [Sample HubSpot User](https://blog.advance.ai/zh-cn/author/sample-hubspot-user) on  Nov 24, 2023, 3:49:14 PM

**作者：胡晓晓  职位：前端工程师**

**背景**

众所周知，JavaScript 是一门单线程语言，同一时刻只能执行一个任务。同步任务都在主线程上执行，形成一个执行栈。遇到异步任务，如果未拿到结果一直等待的话，就会阻塞后续代码执行，这种结果是我们不想看到的。

那么是否存在这么一种机制：遇到异步代码，JavaScript 单线程运行时不会阻塞？答案是肯定的：Event Loop 事件循环就是解决异步阻塞的一种机制。

## **一、基本概念回顾**

在进入本文的整体之前，我们先来回顾一些基本的概念。

### **1、执行和运行**

JavaScript 执行和运行是两个概念：

1. 执行：依赖于环境，如浏览器、node 等等，在不同环境执行机制不尽相同。
2. 运行：JavaScript 的解析引擎。

### **2、浏览器线程**

浏览器内核是多线程的，在内核控制下各线程相互配合以保持同步，一个浏览器通常由以下常驻线程组成：

1. GUI 渲染线程
   
     - 主要负责页面的渲染，解析 HTML、CSS，构建 DOM 树，布局和绘制等。
     - 当界面需要重绘或者由于某种操作引发回流时，将执行该线程。
     - 该线程与 JavaScript 引擎线程互斥，当执行 JavaScript 引擎线程时，GUI 渲染会被挂起，当任务队列空闲时，JavaScript 引擎才会去执行 GUI 渲染。
2. JavaScript 引擎线程
   
     - 主要负责处理 JavaScript 脚本，执行代码。
     - 也是主要负责执行准备好待执行的事件，即定时器计数结束或者异步请求成功并正确返回时，将依次进入任务队列，等待 JS 引擎线程的执行。
     - 该线程与 GUI 渲染线程互斥，当 JavaScript 引擎线程执行 JavaScript 脚本时间过长，将导致页面渲染的阻塞。
3. 定时触发器线程
   
     - 负责执行异步定时器一类的线程，如：setTimeout，setInterval。
     - 主线程依次执行代码时，遇到定时器，会将定时器交给该线程处理，当计数完毕后，事件触发线程会将计数完毕后的事件加入到任务队列的尾部，等待 JS 引擎线程执行。
4. 事件触发线程
   
     - 主要负责将准备好的事件交给 JavaScript 引擎线程执行。
     - 比如 setTimeout 定时器计数结束， ajax 等异步请求成功并触发回调函数，或者用户触发点击事件时，该线程会将整装待发的事件依次加入到任务队列的队尾，等待 JS 引擎线程的执行。
5. 异步 http 请求线程
   
     - 负责执行异步请求一类的线程，如：Promise，axios，ajax 等。
     - 主线程依次执行代码时，遇到异步请求，会将函数交给该线程处理，当监听到状态码变更，如果有回调函数，事件触发线程会将回调函数加入到任务队列的尾部，等待 JavaScript 引擎线程执行。

### **3、执行栈**

JavaScript 执行栈又叫调用栈(LIFO)，用来存储代码运行时创建的所有执行上下文。

--\>当 JavaScript 引擎第一次遇到脚本 script 标签

    --\> 创建一个全局的执行上下文并且压入当前执行栈 

        --\> 当引擎遇到一个函数调用，它会为该函数创建一个新的执行上下文，压入栈的顶部

        --\> 该函数执行结束时，执行上下文从栈中弹出  
        --\> 控制流程到达当前栈中的下一个上下文

        ... ...

## **二、Browser Context 的 Event Loop**

众所周知，JavaScript 是一门单线程语言，有同步任务和异步任务之分。

异步任务回调函数是放在任务队列的。

任务队列有两种：宏任务（macro-task）队列和微任务（ micro-task）队列。

- 宏任务队列是一次只执行一个任务
- 微任务是一次执行当前微任务队列中的所有任务，

 

常见的macro-task 如：setTimeout、setInterval、 setImmediate、script（整体代码）、 I/O 操作、UI 渲染等。

常见的 micro-task 如: process.nextTick、new Promise().then(回调)、MutationObserver 等。

 

**Browser Context 的 Event Loop 解读**

1. 一开始我们的执行栈为空，全局上下文(script标签)被推入执行栈，代码被执行。
2. 代码执行过程中，会判断是同步任务还是异步任务，同步任务会被放入执行栈，立刻执行；异步任务会交给对应的异步处理模块去处理，异步处理完成后，会把异步任务的回调放入任务队列，当前任务执行完后出栈；
3. 如果主线程任务全部执行完成，执行栈为空，接下来我们会检查微任务队列，如果队列不为空，会把微任务队列的回调放入主线程去执行，直到当前微任务队列被清空；
4. 然后执行宏任务队列，宏任务队列是一次只执行一个任务；
5. 3和4循环往复，直到两个队列都清空。

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-31-17-8338-AM.png?width=660&height=819&name=image-png-Nov-24-2023-07-31-17-8338-AM.png)

 

## **三、一道题彻底理清 Event loop**

在 index.html 中有这么一段代码

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-34-00-2067-AM.png?width=658&height=1308&name=image-png-Nov-24-2023-07-34-00-2067-AM.png)

 

当我们点击 inner 时，输出什么？

- click
- promise
- mutate
- click
- promise
- mutate
- timeout
- timeout

 

如果 JavaScript 代码中加一行 inner.click()，又会输出什么呢？  
答案是：

- click
- click
- promise
- mutate
- promise
- timeout
- timeout

 

为什么加了一行代码，输出结果就改变了呢?

上文提到了浏览器内核是多线程的。我们上面这段代码用到了 JavaScript 引擎线程、定时器触发线程以及事件触发线程。

 

先来分析第一段代码

JavaScript 是单线程，顺序执行代码，所以，当 script 标签里的代码执行完成后，执行栈为空：

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-45-00-8900-AM.png?width=661&height=771&name=image-png-Nov-24-2023-07-45-00-8900-AM.png)

如上图所示：执行栈 Call Stack 为空，同时浏览器内核中事件触发线程队列中放入了两个任务。

 

当我们点击 inner 时，会发生什么呢？

1. 点击 inner 时，事件触发线程会把 inner click callback 放入任务队列；发生冒泡，事件触发线程会把outer click callback 放入任务队列；

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-45-39-6763-AM.png?width=654&height=777&name=image-png-Nov-24-2023-07-45-39-6763-AM.png)

 

2\. 检查微任务队列， MicroTask 为空，继续下一步；

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-46-03-7723-AM.png?width=660&height=742&name=image-png-Nov-24-2023-07-46-03-7723-AM.png)

 

3\. 从 MactoTask 取出一个宏任务，放入主线程

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-46-29-8293-AM.png?width=649&height=694&name=image-png-Nov-24-2023-07-46-29-8293-AM.png)

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-46-46-5769-AM.png?width=657&height=707&name=image-png-Nov-24-2023-07-46-46-5769-AM.png)

 

3.1 执行 onClick

遇到同步代码顺序执行，输出 \`click\`

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-47-20-6586-AM.png?width=655&height=398&name=image-png-Nov-24-2023-07-47-20-6586-AM.png)

 

执行完出栈

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-47-43-4369-AM.png?width=657&height=721&name=image-png-Nov-24-2023-07-47-43-4369-AM.png)

 

3.2 遇到 setTimeout 放入定时器触发线程，当 setTimeout 的 callback 触发时，会把setTimeout callback 放入 MacroTask 队列

 

3.3 Promise.then 和 MutationObserver 放入微任务队列；

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-48-16-4196-AM.png?width=661&height=463&name=image-png-Nov-24-2023-07-48-16-4196-AM.png)

 

3.4 当前任务处理完成，onClick 出栈

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-48-36-9104-AM.png?width=650&height=473&name=image-png-Nov-24-2023-07-48-36-9104-AM.png)

4\. 然后继续下一轮的Event Loop(事件循环)；此时检查微任务队列，微任务队列不为空

4.1 取出一个微任务，放入执行栈，执行代码，此时会输出 \`**promise** \`，执行完出栈

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-54-12-4492-AM.png?width=751&height=751&name=image-png-Nov-24-2023-07-54-12-4492-AM.png)

 

4.2 继续清空微任务队列，重复上一步步骤，此时会输出 \`mutate\` 。

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-54-33-7073-AM.png?width=754&height=804&name=image-png-Nov-24-2023-07-54-33-7073-AM.png)

 

5\. 取出宏任务队列中的一个执行，步骤同3；

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-54-56-7777-AM.png?width=758&height=714&name=image-png-Nov-24-2023-07-54-56-7777-AM.png)

 

6\. 继续下一轮事件循环，清空微任务队列，同步骤4；

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-55-22-2701-AM.png?width=756&height=752&name=image-png-Nov-24-2023-07-55-22-2701-AM.png)

 

7\. Macro-tasks 任务队列取出一个任务，放入任务队列

- 同步任务继续执行

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-55-39-4936-AM.png?width=752&height=845&name=image-png-Nov-24-2023-07-55-39-4936-AM.png)

 

- 执行完成出栈

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-55-58-3797-AM.png?width=752&height=837&name=image-png-Nov-24-2023-07-55-58-3797-AM.png)

 

8\. 又一轮event loop 开始，此时微任务队列为空；

 

9\. 从宏任务队列取出一个任务执行，同步骤7，执行完成后出栈，此时宏任务队列为空。

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-56-16-1471-AM.png?width=746&height=893&name=image-png-Nov-24-2023-07-56-16-1471-AM.png)

 

最终输出结果如上图所示。

**inner.click() 发生了什么**

与上一段代码不同，执行 inner.click 之后，Call Stack 队列不为空：

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-57-32-6127-AM.png?width=755&height=463&name=image-png-Nov-24-2023-07-57-32-6127-AM.png)

 

**1. 执行栈 inner 的click callback先被执行**

- 首先是 console.log('click')，同步代码立刻执行，输出\`click\`
- 执行到 setTimeout，异步代码，交给定时器触发线程去处理，处理完成后callback放入MacroTasks
- 执行到 Promise.then 异步代码，放入微任务队列 MicroTasks ；
- 执行到 MutationObserver 异步代码，放入微任务队列 MicroTasks ；

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-07-58-09-7870-AM.png?width=757&height=691&name=image-png-Nov-24-2023-07-58-09-7870-AM.png)

- inner 的click callback代码执行完成，出栈

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-08-00-40-4543-AM.png?width=749&height=658&name=image-png-Nov-24-2023-08-00-40-4543-AM.png)

 

2.主线程执行栈不为空，继续执行Call Stack的下一个方法， outer 的click callback 方法被执行，同1的步骤，outer 的callback执行中有一点需要注意，微任务队列中最多只能有一个MutationObserver，所以执行完成后结果如下：

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-08-01-34-1658-AM.png?width=736&height=662&name=image-png-Nov-24-2023-08-01-34-1658-AM.png)

 

3\. 此时 inner.click 代码执行完毕，出栈

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-08-02-08-7180-AM.png?width=747&height=725&name=image-png-Nov-24-2023-08-02-08-7180-AM.png)

 

4\.  检查微任务队列，微任务队列不为空

4.1 取出一个微任务，放入执行栈，执行代码，此时会输出 \`**promise** \`，执行完出栈

4.2 继续清空微任务队列，重复上一步步骤，一直到清空微任务队列，此时会依次输出 mutaion promise

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-08-02-47-2526-AM.png?width=754&height=698&name=image-png-Nov-24-2023-08-02-47-2526-AM.png)

 

5\. 宏任务队列不为空，取出第一个任务放入执行栈执行，此时输出 **timeout**

 

**![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-08-03-02-8122-AM.png?width=741&height=710&name=image-png-Nov-24-2023-08-03-02-8122-AM.png)**

 

6\. 重复4和5 进入下一次事件循环，此时微任务队列为空；

7\. 宏任务队列不为空，取出第一个任务放入执行栈，同5

 

![](https://blog.advance.ai/hs-fs/hubfs/image-png-Nov-24-2023-08-03-27-1385-AM.png?width=747&height=735&name=image-png-Nov-24-2023-08-03-27-1385-AM.png)

 

8\. 继续重复4和5 ，直到宏任务队列和微任务队列都为空。

 

## **三、结束语**

本文旨在帮助大家理解浏览器的 Event Loop ，以图文的形式呈现给大家，所以图会比较多，如有不同见解，欢迎大家一起探讨。

 

 

[Back to Blog](https://blog.advance.ai/zh-cn)

## Related Articles

<https://blog.advance.ai/zh-cn/distributed-websocket-service>

## [分布式WebSocket服务的挑战和一个简单实现](https://blog.advance.ai/zh-cn/distributed-websocket-service)

 作者：董礼 职位：后端工程师

[Read More](https://blog.advance.ai/zh-cn/distributed-websocket-service)

<https://blog.advance.ai/zh-cn/springcloud-architecture-breakpoint-debugging>

## [k8s集群中的springcloud架构如何丝滑的断点调试](https://blog.advance.ai/zh-cn/springcloud-architecture-breakpoint-debugging)

 作者：马铁强 职位：QA Lead

[Read More](https://blog.advance.ai/zh-cn/springcloud-architecture-breakpoint-debugging)

<https://blog.advance.ai/zh-cn/application-dubbo-service-0>

## [Dubbo3.0应用级服务注册原理](https://blog.advance.ai/zh-cn/application-dubbo-service-0)

 作者：周起仝 职位：后端工程师

[Read More](https://blog.advance.ai/zh-cn/application-dubbo-service-0)

[![wordmark-white-01](https://blog.advance.ai/hs-fs/hubfs/AAI%20logo/wordmark-white-01.png?width=3809&name=wordmark-white-01.png "wordmark-white-01")](http://demo2019.test.yongsy.net/zhixincn/index.html)

- 友情链接 
    - [领创集团](https://www.advancegroup.com.cn/)
- 核心产品 
    - [AI 生物识别验证](https://www.advanceai.com.cn/liveness-detection)
    - [AI 文档验证](https://www.advanceai.com.cn/id-document-recognition)
- 解决方案 
    - [星鉴解决方案](https://www.advanceai.com.cn/xingjian)
    - [视频身份验证](https://www.advanceai.com.cn/video-identification)
    - [更多部署方案](https://www.advanceai.com.cn/deployment-options)
- 行业应用 
    - [金融服务](https://www.advanceai.com.cn/financial-services)
    - [银行业](https://www.advanceai.com.cn/banking)
    - [旅游出行](https://www.advanceai.com.cn/travel)
    - [共享经济](https://www.advanceai.com.cn/sharing-economy)
    - [电子商务](https://www.advanceai.com.cn/e-commerce)
    - [金融科技](https://www.advanceai.com.cn/fintech)
- 关于我们 
    - [公司简介](https://www.advanceai.com.cn/about-advance-ai)
    - [安全团队](https://www.advanceai.com.cn/security-team)
    - [合作伙伴](https://www.advanceai.com.cn/partnership)
    - [新闻动态](https://www.advanceai.com.cn/company-news)
    - [加入我们](https://www.advanceai.com.cn/join-us)

© 2024 ADVANCE.AI. 请参阅领创智信的[服务条款](https://beta.advance.ai/terms-of-service)和[隐私和安全声明](https://beta.advance.ai/privacy-security-statement)

[![Contact Icon](https://blog.advance.ai/hs-fs/hubfs/raw_assets/public/AdvanceIntelligence_October2020/images/icon1.png?width=180&name=icon1.png "Contact Icon")](https://info.advance.ai/cn-free-trial)

[![Message Icon](https://blog.advance.ai/hs-fs/hubfs/raw_assets/public/AdvanceIntelligence_October2020/images/icon2.png?width=180&name=icon2.png "Message Icon")](https://info.advance.ai/zh-cn/advance.ai-lianxiwomen)

![Back To Top](https://blog.advance.ai/hubfs/raw_assets/public/AdvanceIntelligence_October2020/images/icon4.png)