惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
GbyAI
GbyAI
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
Recent Announcements
Recent Announcements
L
LangChain Blog
B
Blog
阮一峰的网络日志
阮一峰的网络日志
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
D
Docker
WordPress大学
WordPress大学
J
Java Code Geeks
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The GitHub Blog
The GitHub Blog
博客园 - 叶小钗
Last Week in AI
Last Week in AI
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
MyScale Blog
MyScale Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MongoDB | Blog
MongoDB | Blog
博客园 - Franky

Sanonz

使用 Invox Setup 实现 Electron 安装包界面美化 用 VS Code 编辑器开发 STM32 嵌入式 使用 FontCreator 编辑字体库 | Sanonz 使用 React Context API 和 Hooks 实现全局状态管理和性能优化 使用 Phonegap + Cordova 搭建混合开发平台 利用 Github Actions 自动部署 Hexo 博客 在 WEB 端实现亮/暗主题跟随系统功能 | Sanonz Metalness vs Specular Workflows for PBR Shading 以太坊概念与使用 wep3/infura 实现以太币及合约币交易流程 | Sanonz 实现树莓派(Raspberry Pi)联网发送本机IP到绑定的微信 | Sanonz 使用 Three.js 的 RenderTarget 实现离屏渲染 使用 redux-store-provider 简化 react 开发流程 浏览器 HTTP 并发请求规则探讨 | Sanonz WebAssembly 从入门到吃鸡 | Sanonz Let's Encrypt 使用教程,免费的 SSL 证书,让你的网站拥抱 HTTPS Let's Encrypt 使用教程,免费的 SSL 证书,让你的网站拥抱 HTTPS Ubuntu 搭建 php、mysql、nginx 开发环境 | Sanonz WebGL 三维教程 - 绘制矩形和点(二) | Sanonz WebGL 三维教程 - WebGL 介绍(一) Hexo 搭建静态博客与 hexo-theme-concise 主题使用教程 | Sanonz
JavaScript Event Loop、Micro Task、Macro Task
Sanonz · 2018-02-24 · via Sanonz

Event Loop

Event Loop:事件循环、Micro Task:微任务、Macro Task:宏任务

我们知道 JavaScript 是单线程语言,一心不能二用,也就是说它只能把一件事干完才能去干另一件事,但前端的一些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让它们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以为了保证使用流畅,浏览器采用了 Event Loop 事件循环系统来管理,把那些耗时的任务放入 task queues 栈中,等主程序执行完再执行 task queues 中的任务,通常使用回调函数监听 task queues 中的任务状态。

在其它语言中可以迟延执行,比如 C 语言的 sleep(3) 可以迟延 3 秒执行后边的逻辑,但是在 JavaScript 中是没有这样的操作(除了 alertconfrimprompt 和异步 xhr,官方说这些是历史遗留的错误设计😂),只能使用异步方式去模拟,模拟的实际也不是真正意义上的 sleep 效果。

示例代码

在讨论 Micro Task、Macro Task 之前我们先来看一个例子,分析以下代码并思考 log 打的印顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log('script start');

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

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

console.log('script end');

运行结果为

1
2
3
4
5
script start
script end
promise 1
promise 2
setTimeout 1

有人可能会有疑问,为什么不是按顺序输出?又或者是这样

1
2
3
4
5
script start
script end
setTimeout 1
promise 1
promise 2

Micro Task、Macro Task

想要搞明白这个问题,首先要了解 Event Loop 下的 Micro TaskMacro Task,在运行主程序时会把异步逻辑根据情况推到 Micro Task 或者 Macro Task 栈中,具体规则如下

以下情况会推到 Micro Task 栈中

  • process.nextTick
  • Promise
  • Object.observe
  • MutationObserver

以下情况会推到 Macro Task 栈中

  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI render

然后再来看实例代码的实际执行逻辑

  1. 执行 console.log('script start') 直接输出
  2. 执行 setTimeout 把回调函数放入 Macro Task 栈中
  3. 执行 Promise 把两个 then 的回调函数放入 Micro Task 栈中
  4. 执行 console.log('script end') 直接输出
  5. 执行 Micro Task 中所有任务
    1. 执行 console.log('promise 1') 直接输出
    2. 执行 console.log('promise 2') 直接输出
  6. 执行 Macro Task 中所有任务
    1. 执行 console.log('setTimeout 1') 直接输出

结束语

事件循环的顺序,决定了 JavaScript 代码的执行顺序。它从 script (整体代码) 开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的 Micro Task。当所有可执行的 Micro Task 执行完毕之后。循环再次从 Macro Task 开始,找到其中一个任务队列执行完毕,然后再执行所有的 Micro Task,在执行 Micro Task、Macro Task 的时候同样遵循 Event Loop 原则,就这样一直循环下去。

参考站点