「EventLoop」图文并茂-带你完全搞懂事件循环♻️

2,068 阅读8分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金

大家好,我是速冻鱼🐟,一条水系前端💦,喜欢花里胡哨💐,持续沙雕🌲,是隔壁寒草🌿的好兄弟,刚开始写文章。 如果喜欢我的文章,可以关注➕点赞,为我注入能量,与我一同成长吧~

温馨提示☀️:文章中有大量GIF图片,有可能会加载比较慢,小伙伴们耐心等待一下(我尽力压缩了呜呜)

阅读本文🦀

1.您将了解到事件循环到底是什么

2.为什么javascript是单线程,却可以执行异步函数

3.为什么浏览器要采用事件循环

4.您将了解到什么是javaScript运行时

5.您将了解到事件循环和浏览器重绘之间的联系

前言🌵

JavaScript 程序员喜欢使用诸如“事件循环”、“非阻塞”、“回调”、“异步”、“单线程”和“并发”之类的词。好像这一切都很明显,但是要找到 JavaScript 实际工作原理的良好解释并不是那么容易,所以让我们学习吧!🕶

前置知识👋

什么是javaScript

JavaScript 是一种解释型语言,而不是一种编译型语言。像 C++ 或 Java 这样的程序在运行之前需要进行编译。源代码通过称为编译器的程序传递,编译器将其转换为机器理解并可以执行的字节码。相比之下,JavaScript 没有编译步骤。相反,浏览器中的解释器会读取 JavaScript 代码,解释每一行,然后运行它。更现代的浏览器使用一种称为即时 (JIT) 编译的技术,该技术将 JavaScript 编译为即将运行的可执行字节码。

  • 单线程 (相当于一个车间只有一个工人)
  • 单并发 (同一时间只能干一件事)
  • 有一个调用堆栈 (javaScript执行上下文)
  • 一个事件循环 (从回调队列中获取回调函数,并将它们放入调用堆栈中,以便执行它们。)
  • 一个回调队列(是一个数据结构,包含所有准备执行的回调函数。)
  • 一些其他API

什么是javaScirpt运行时

javaScript是运行在V8引擎中的,V8 Google 的开源高性能 JavaScriptWebAssembly 引擎,用 C++ 编写。

  • heap(堆内存)主要存放我们的对象
  • stack (栈内存)是我们javaScript的执行上下文环境(execution contexts),还有一些基本数据类型

什么是WebAPI

浏览器提供的一些额外的东西,比如DOM、AJAX、TIMEOUT等等

Web API 是提供给引擎的功能,但不是 JavaScript 语言本身的一部分。

什么是事件循环

事件循环是一种编程构造或设计模式,用于在程序中等待和分派事件消息。事件循环的工作原理是向某个内部或外部“事件提供者”发出请求(通常在事件到达之前阻止请求),然后调用相关的事件处理程序(“调度事件”)。事件循环有时也称为消息调度程序消息循环消息泵运行循环

正文 🦁

将我们上面提到的这些东西组合起来就是我们今天要了解的事件循环EventLoop

javaScript执行机制

各位小伙伴先看个图,有个大致印象,知道我要说什么,后边都会一一讲解的。

JavaScript是一种单线程编程语言,单线程运行时,它具有单个调用堆栈。

它一次只能做一件事,这就是单线程的意思,程序一次可以运行一段代码。还是直接看图吧。

所以调用堆栈基本上它是一个数据结构,它基本上记录了我们在程序中的位置,如果我们进入一个函数,我们把这个函数放到堆栈中,如果我们从一个函数返回,我们从堆栈的顶部弹出它。

这就是我们的程序出错的时候,你为什么会在控制台看到诸如此类的提示一样

相信大家也有遇到过堆栈内存溢出的情况吧,这种嵌套死循环,就会不停的在我们的堆栈开辟内存空间最后溢出,导致报错

当我们的同步代码执行速度很慢的时候

下面我们模拟我们的网络请求是同步的

如何我们的所有代码都是同步执行的,那么就会阻塞我们的线程,当我们的线程被阻塞的时候我们什么也做不了,只有慢慢等待,显然发起网络请求这种操作如果是同步的,那将会带来很严重的后果。但是javaScript又是单线程的,怎么解决呢?

当然是将这种耗时的操作异步执行了呀(比如AJAXTimeOut等等),这时候你可能会说了,上次才说了javaScript是单线程的,又怎么执行异步函数呢?

这一点浏览器早就想到了,AJAX、TimeOut等等这些WebAPIs都是浏览提供给javaScript引擎的,它并不属于javaScript语言的一部分。

当我们执行异步函数的时候我们就会把它交给浏览器来管理,就有了我们下面这张图

WebAPIs:

就是浏览器提供给我们一些执行异步函数的API,它是由浏览器来管理的,和我们的javaScript执行调用栈是隔离开的。

Callbak Queue:

是我们的异步函数回掉队列,当我们异步函数可以执行的时候,浏览器就会将它的回掉函数推送到我们的Callback Queue中

EventLoop :

当我们的javaScript执行栈中没有要执行的语句时,EventLoop就会从我们的队列中依次取出队头的回调函数,将它推送到我们的Stack中,等待Stack执行完毕,继续取队头的回调函数,如此往复。

示例 🐳

示例1 设置一个定时器 ⌚️

  • 首先控制台打印 Hi
  • 执行到第二句setTimeout,这时候交给我们的浏览器来处理,浏览器会为你开启一个定时器,它不在javaScript运行时中。
  • 执行第三句,打印 JSConfEu
  • 五秒过后,浏览器将我们的setTimeout回调函数推到 CallBack Queue(task queue)队列中,当stack执行栈中没有需要执行的语句后,evnet Loop就会从我们的 CallBack Queue中取出回调函数执行栈中去执行。
  • 最后打印出 there

Hi=>JSConfEui=>there

即使你将SetTimeout设置为0,它也是会按照上面的步骤依次执行

示例2 发送一个异步网络请求 🍪

  • 首先打印 Hi
  • 然后发送异步网络请求,交给浏览器来处理。
  • 执行第三句,打印 JSConfEU
  • 当请求完成后,浏览器将它推入task queue任务队列中(也可以叫callback queue)
  • 当stack没有要执行的语句时,eventloop从task queue中取出网络请求的回调函数推入stack中执行
  • 最后打印出网络请求的结果

事件循环和浏览器重绘的关联 🌽

  • 我们需要知道很关键的一点是如果我们的堆栈上有代码还未执行完,我们的浏览器就无法进行渲染,要渲染必须等到堆栈清除为止
  • 渲染的优先级别高于回调函数的优先级别

下图gif不是很清楚,我把代码贴在这里

image-20211006172309962

当我们执行栈中的函数执行完成以后,我们就会先进行一次重绘,然后再去回调队列中去取出回调函数到堆栈中执行,如此往复

参考 📚

收获 🍁

在这篇文章中我们了解到了什么是事件循环,以及JavaScript的运行机制,什么是V8,什么是WebAPI等等,学习底层的原理能让我开拓视野,能够从根源去了解javaScript本质,能快速定位问题,也希望您能在这篇文章中有所收获,希望这篇文章对你有所帮助

结束语 🌞

那么我的第N篇文章就结束了,文章的目的其实很简单,就是对日常工作的总结和输出,输出一些觉得对大家有用的东西,菜不菜不重要,但是热爱🔥,希望大家能够喜欢我的文章,我真的很用心在写,也希望通过文章认识更多志同道合的朋友,如果你也喜欢折腾,欢迎加我好友,一起沙雕,一起进步

github🤖:sudongyu

个人博客👨‍💻:速冻鱼blog

vx👦:sudongyuer

写在最后

伙伴们,如果喜欢我的口水话给🐟🐟点一个赞👍或者关注➕都是对我最大的支持。

加我微信:sudongyuer,邀你进群,一起学习前端,成为更优秀的工程师~(群二维码在这里->前端要早睡, 二维码过期了的话看链接沸点中的评论,我会把最新的二维码放在评论区,当然也可以加我微信我拉你进群,毕竟我也是有趣的前端,认识我也不赖🌟~)