JavaScript的Event Loop(浏览器)

春节的时候看到奇舞周刊发的关于Event Loop的文章https://mp.weixin.qq.com/s/KEl_IxMrJzI8wxbkKti5vg,看的也是迷迷糊糊。

昨天准备写一下几个Promise的小例子,发现理解起来还是要懂得Event Loop,所以又在网上找了几篇文章看了一下,发现各有各的说法,不过关于基础的部分都是一致的,

本着学习记录的心态,我这里简单地把我对Event Loop的理解说明一下,当然还是有很多不到位的地方,欢迎大家一起交流学习。

首先我们来看一张图片:(《Learning TypeScript》)

  • Heap 堆是一个内存存储空间,保存了正在被使用的对象,同时也保存在当前作用域已经不再会被用到的,但还没有被垃圾回收的frame(帧)。
  • Stack 栈,有文章称为调用栈或者执行栈,是一种后进先出的数据结构(只可以在栈顶进行操作)。主要存放Frame(帧)以及对应帧的一些基础类型变量和对象的指针。
  • Frame 帧,存放在Stack中(Stack中的小块),可以理解为一个个函数。
  • Queue 队列,是一种先进先出的数据结构(在队列前面弹出数据,在队列尾部填充数据)。待执行信息(函数)的列表,当Stack为空时(Frame被执行完成),Queue的最前面的信息会被提取到Stack中进行处理。

比如有一个文件test.js

 1 function A() {
 2   console.log(‘A start‘)
 3   Promise.resolve().then(
 4     () => console.log(‘A 的 Promise then‘)
 5   )
 6   console.log(‘A end‘)
 7 }
 8
 9 function B() {
10   console.log(‘B start‘)
11   setTimeout(
12     () => console.log(‘B 的 setTimeout‘),
13     0
14   )
15   A();
16   console.log(‘B end‘)
17 }
18
19 function C() {
20   console.log(‘C start‘)
21   B();
22   console.log(‘C end‘)
23 }
24 C();

运行结果

  1. C start
  2. B start
  3. A start
  4. A end
  5. B end
  6. C end
  7. A 的 Promise then
  8. B 的 setTimeout

在运行到调用C函数时,C函数这个帧会被首先加入到栈中,这时它是栈顶,栈中只有它一个帧。队列此时是空的。

执行C函数(打印出C start)后,发现C函数调用了B函数,这时就把B函数推入栈顶。

执行B函数(打印出B start,将setTimeout的回调函数放在任务队列中,这时任务队列有一个任务)后, 发现B函数调用了A函数,这时就把A函数推入栈顶。

执行A函数,打印出A start ,将Promise的then方法的回调函数放在任务队列中,这时任务队列就有了2个任务。继续执行A函数,直到打印出A end,

这时A函数执行完毕,A函数被弹出栈顶(这时栈中有2个帧 B和C),执行交还给B函数,B函数继续执行,直到打印出B end,

这时B函数执行完毕,B函数被弹出栈顶(这时栈中有1个帧 C),执行交还给C函数,C函数继续执行,直到打印出C end,

这时C函数执行完毕,C函数被弹出栈顶(这时栈中有0个帧,也就是说栈被清空了),

栈被清空之后,系统会去任务队列中找任务(此时任务队列中有2个任务,一个是B函数添加进去的setTimeout的回调函数,一个是A函数添加进去的Promise的then方法的回调函数),

正常情况下,因为队列是先进先出的数据结构,B函数添加进去的setTimeout的回调函数比A函数添加进去的Promise的then方法的回调函数要早(其实也不早,不过这涉及到异步操作的问题,这里暂时这样理解,对于理解Event Loop没有影响)。

理所当然的以为会先打印出B 的 setTimeout,其实并没有,这里涉及到任务的分类,任务分为2类:

  1. MacroTask(宏任务)script全部代码、setTimeout、setInterval、setImmediate
  2. MicroTask(微任务)Promise、Observable

很显然,

B函数添加进去的setTimeout的回调函数进入宏任务队列中。A函数添加进去的Promise的then方法的回调函数进入微任务队列中。

需要明确的是,JS的机制是等到栈中为空时,会先去微任务队列寻找任务,根据先进先出的原则把微任务队列中的任务添加到栈中并执行,直到微任务队列为空时才会去宏任务队列中寻找任务。

所以出现了先打印A 的 Promise then 后打印出B 的 setTimeout

下面用图形解释一下(左边是代码执行到的位置,中间是堆栈信息,右边是console打印出来的信息)

上面就是EventLoop最基本的原理,要记住的是

  1. 要等到栈清空会才会执行任务队列中的任务
  2. 根据1,得到同步任务即使花费的时间再长也比异步任务早执行
  3. Promise的回调比setTimeout的回调要早

其实上面的只是JS的主线程的逻辑,如果放在浏览器中执行,中间还会有JS线程移交执行权给UI线程渲染页面的过程,这个是另外的话题,之后我会单独写文章再介绍。

2019-02-10

参考:

https://www.cnblogs.com/cangqinglang/p/8967268.html

https://mp.weixin.qq.com/s/KEl_IxMrJzI8wxbkKti5vg

原文地址:https://www.cnblogs.com/wangtingnoblog/p/js_EventLoop.html

时间: 2024-10-31 08:52:52

JavaScript的Event Loop(浏览器)的相关文章

理解Javascript的Event Loop

一.单线程 js作为浏览器脚本语言,他的主要用途是与用户交互,以及操作DOM,这决定了它只能是单线程,为什么呢?因为假如js同时有两个线程,一个线程是在DOM上增加内容,另一个线程是删除这个节点,那么这时候浏览器应该以哪个线程为主呢?所以为了避免复杂性,js的核心特性就是单线程. 二.任务队列 单线程就意味着所有任务都要排队,前一个任务完成之后,下一个任务才会被执行,否则后一个任务就一直登等待. 任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronou

javascript的执行机制—Event Loop

既然今天要谈的是javascript的事件循环机制,要理解事件循环,首先要知道事件循环是什么. 我们先从一个例子来看一下javascript的执行顺序. <script> setTimeout(function() { console.log('定时器开始了.'); },0) new Promise(function(resolve) { console.log('马上执行for循环了'); for (let i = 0; i < 10000; i++) { i == 99 &&

【朴灵评注】JavaScript 运行机制详解:再谈Event Loop

PS: 我先旁观下大师们的讨论,得多看书了~ 别人说的:“看了一下不觉得评注对到哪里去,只有吹毛求疵之感. 比如同步异步介绍,本来就无大错:比如node图里面的OS operation,推敲一下就可以猜到那是指同步操作(自然不走event loop了):至于watcher啥的,显然只是实现上的特色,即使用同一个queue实现也未尝不可” [原帖: http://www.ruanyifeng.com/blog/2014/10/event-loop.html 作者:阮一峰] 一年前,我写了一篇<什么

【repost】JavaScript 运行机制详解:再谈Event Loop

一年前,我写了一篇<什么是 Event Loop?>,谈了我对Event Loop的理解. 上个月,我偶然看到了Philip Roberts的演讲<Help, I'm stuck in an event-loop>.这才尴尬地发现,自己的理解是错的.我决定重写这个题目,详细.完整.正确地描述JavaScript引擎的内部运行机制.下面就是我的重写. 进入正文之前,插播一条消息.我的新书<ECMAScript 6入门>出版了(版权页,内页1,内页2),铜版纸全彩印刷,非常

JavaScript 运行机制详解:再谈Event Loop

原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Event Loop的理解. 上个月,我偶然看到了Philip Roberts的演讲<Help, I'm stuck in an event-loop>.这才尴尬地发现,自己的理解是错的.我决定重写这个题目,详细.完整.正确地描述JavaScript引擎的内部运行机制.下面就是我的重写. 进入正文之前,

Javascript Event Loop

Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制. JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题. 本文参考C. Aaron Cois的<Understanding The Node.js Event Loop>,解释什么是Event Loop,以及它与JavaScript语言的单线程模型有何关系. 想要理解Event Loop,就要从程序的运行模式讲起.运行以后的程序叫做"进程"(process),一般情况下,一个进程一次

深入理解Javascript单线程谈Event Loop

假如面试回答js的运行机制时,你可能说出这么一段话:"Javascript的事件分同步任务和异步任务,遇到同步任务就放在执行栈中执行,而碰到异步任务就放到任务队列之中,等到执行栈执行完毕之后再去执行任务队列之中的事件."但你能说出背后的原因吗? 1.线程与进程 进程:是系统资源分配和调度的单元.一个运行着的程序就对应了一个进程.一个进程包括了运行中的程序和程序所使用到的内存和系统资源. 线程:线程是进程下的执行者,一个进程至少会开启一个线程(主线程),也可以开启多个线程. 2.同步和异

(转载)JavaScript 运行机制详解:再谈Event Loop

source: http://www.ruanyifeng.com/blog/2014/10/event-loop.html  一年前,我写了一篇<什么是 Event Loop?>,谈了我对Event Loop的理解. 上个月,我偶然看到了Philip Roberts的演讲<Help, I'm stuck in an event-loop>.这才尴尬地发现,自己的理解是错的.我决定重写这个题目,详细.完整.正确地描述JavaScript引擎的内部运行机制.下面就是我的重写. 进入正

JavaScript 运行机制详解:Event Loop

转自: http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM.这决定了它只能是单线程,否则会带来很复杂的同步问题.比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个