前言
首先,需要介绍一下 JavaScript 这门语言的运行机制和内存管理以便于更好的理解事件循环机制。
很多同学肯定知道 JavaScript 是一门单线程、非阻塞、异步、解释性的脚本语言。还有很明显我们都在使用回调函数,但是你知道回调函数是怎么运行的吗?首先看一下来自 MDN 的这张图片,你会得到如下概念:
- JavaScript 的函数在调用时他本身和函数体中的代码会按照调用顺序被压入栈(stack)中,执行完立马被弹出。
- 对象是存储在堆(heap)中的,堆是一个用来表示一大块(通常是非结构化的)内存区域的计算机术语。
- 在 JavaScript 运行时中存在一个一个待处理的消息队列(queue),每一个消息都关联着一个用以处理这个消息的回调函数。
所以,你应该对没有结束条件的递归造成的死循环原理有个认知:函数不断的被加入到执行栈,在被弹出,然后调用了自己,再被加入执行栈,再被弹出,无限重复。。。
相信你肯定看到过下面这张图片,这张图片出自 菲利普·罗伯茨关于事件循环的一次演讲。由于讲的非常通俗易懂,我这边强烈建议先看完这个视频在继续阅读下文。我为什么还要继续写本文呢,那是因为我想水文章[斜眼笑]。额,因为这个演讲是 2014 年,当时还有明确微任务和宏任务相关的概念。我会在下文补充相关知识点。

相信你已经看完上面的演讲了,你应该十分的清楚 JavaScript 代码是如何运行的了。
首先,你可以简单的认为事件循环是这样工作的:
while(true) {const task = taskQueue.pop();execute(task);}
它就是一个「无限循环」,它从任务队里中抓取一个任务,并执行这个任务。
但是,什么是task? task 就是如何运行 JavaScript 。
其实 微任务 指的是有引擎发起的异步任务,而 宏任务 是指由宿主环境发起的异步任务。所以在浏览器宿主环境下常见的宏任务和微任务如下:
微任务:new Promise().then(回调)、MutationObserver
宏任务(基本上webapi都是):setTimeout()、setInterval()、postMessage、I/O、UI交互事件
后来我又在 YouTube 发现了一个演讲,进一步探索事件循环机制,这个演讲时间是 2018 年,我写本文是在 2021 年 3 月份,不由的感叹我作为一个程序员竟然没有充分利用搜索引擎。当然这是因为我英文水平实在太差。你可以结合这两个演讲将 JavaScript 事件循环机制理解的很好了。但是本文我也不删除了,一方面是作为一个记录,另一方面演讲都是在外网的,也给没有「梯子」的同学做个参考吧。
参考
- How JavaScript Timers Work
- JavaScript代码执行可视化工具
- 菲利普·罗伯茨关于 Event loop 演讲
- JavaScript 中的事件循环机制
- 并发模型与事件循环
- 进一步探索事件循环机制
- 做一些动图,学习一下EventLoop
恶补JavaScript基础系列
恶补JavaScript基础系列目录地址:https://www.sixtyden.com/archive。
恶补JavaScript基础系列是我在从学校毕业入坑前端的学习产物,它主要是我看完书以及其他资料后的一个浓缩总结。以下是我参考的主要资料:
- JavaScript高级程序设计
- 你不知道的JavaScript(上卷)
- 陪你读书(JavaScript web前端)
- 王福朋的博客
- 冴羽写博客的地方
- 汤姆大叔深入 JavaScript 系列
- Facebook 的 ECMAScript 理论专家 Dmitry Soshnikov(最优秀的ECMAScript解读)
本人能力有限,如果有错误或者不严谨的地方,请务必给予指出,十分感谢!愿与君共勉。
(完)
