资讯专栏INFORMATION COLUMN

[译]JavaScript的调用栈、回调队列和事件循环

YancyYe / 827人阅读

摘要:在这个视频中,将的调用栈回调队列和事件循环的内容讲的很清晰。调用栈可以往里面放东西,可以在事件结束的时候把回调函数放进回调队列,然后是事件循环。为的时候这个过程看起来可能不明显,除非考虑到调用栈的执行环境和事件循环的情况。

译者按
这篇文章可以看做是对Philip Roberts 2014年在JSConf演讲的《What the heck is the event loop anyway?》的一个总结。
建议先看Philip Roberts的这个演讲然后再阅读本篇文章。这哥们儿的演讲语言幽默风趣,内容通俗易懂,非常值得一看。

在这个视频中,Philip Roberts将JavaScript的调用栈、回调队列和事件循环的内容讲的很清晰。所以你可以随意的跳过这篇文章,花上一个半小时去看视频。当然如果你愿意读一下我的这篇文章那也不是不可以。

什么是JavaScript

什么是JavaScript呢?列举一些关键词就是:

他是单线程的、非阻塞的、异步的并发语言

他有一个调用栈,一个事件循环,一个回调队列,还有一些api和别的东西

如果你像我一样(或者像Philip Roberts)对此懵逼的话,这些话本身并没不意味着什么。那我们就来剖析一下。

JavaScript运行时

JavaScript运行时(像V8引擎)拥有一个堆(内存分配用的)和栈(执行上下文)。但是他没有setTimeoutDOM等。这些是浏览器提供的Web APIs

我们了解的JavaScript

浏览器中的JavaScript拥有:

一个像V8引擎一样的运行时(提供堆栈)

浏览器提供的Web APIs,例如:DOMAJAXsetTimeout

一个为各种事件回调准备的回调队列,例如:onClickonLoadonDone

一个事件循环

什么是调用栈

JavaScript是单线程的,意味着他有一个多带带的调用栈,意味着他一次能做一件事。调用栈基本上就是一个记录程序执行位置的数据结构。如果程序进入了一个函数,那就往这个栈里面塞些东西。如果程序从一个函数中return了,那就从栈顶弹出一些东西。

当我们的程序报错的时候,我们会在控制台看到调用栈信息。报错的时候我们可以看到栈的状态(被调用的那个函数的)。

阻塞

这涉及到一个重要的问题:程序运行的很慢的时候发生了什么?换句话说,就是程序阻塞了。阻塞并没有严格的定义。实际上就是程序执行慢。执行console.log不慢,但是一个从1到1,000,000,000的while循环,图像处理或者网络请求这些操作的执行就比较费时了。这些执行慢的东西堆在一起就发生了阻塞。

因为JavaScript是单线程的,我们发起一个网络请求就不得不一直等到他结束。这在浏览器中就是个问题--当我们等这个请求的时候,浏览器就发生了阻塞(我们不能做点击、提交表单等操作)。解决这个问题的方法就是使用异步回调。

并发,看到这个词的时候我们会发现上面有一个地方说的不对

JavaScript一次只能做一件事情的说法是不对的。正确的说法应该是:JavaScript的运行时一次只能做一件事。他不能一边发ajax请求一边运行别的代码,也不能在执行别的代码时候运行一个定时器。但是我们可以并发的做这些事。因为浏览器不仅仅是一个运行时(还记得上面那个渣渣画质的图吗?)。

调用栈可以往Web APIs里面放东西,Web APIs可以在事件结束的时候把回调函数放进回调队列,然后是事件循环。最终我们进入事件循环,这是这个过程中最简单的部分,他有一个非常简单的工作:看看调用栈,瞅瞅回调队列,如果调用栈空闲了,就把回调队列中的第一个函数取出来丢进调用栈让他执行(这就回到了JavaScript的地盘,回到了V8的内部)。

整个串起来

Philip搞了一个的碉堡的工具来可视化这个过程,这玩意儿叫Loupe。这是一个能够把JavaScript运行时可视化的工具。

我们用它来看一个简单的例子:在一个异步的setTimeout回调中用console.log在控制台打些log出来。

整个过程到底都发生了什么呢?我们来看一下:

执行进入console.log("Hi");函数,因此这个函数被丢进了调用栈里。

console.log("Hi");函数return了,因此他就被弹出了栈顶。

执行进入setTimeout函数,因此这个函数被丢进了调用栈里。

setTimeoutWeb APIs的一部分,因此Web APIs处理了他,并且等了2秒

继续执行脚本,进入console.log("EvenyBody")函数,把他也丢进调用栈。

console.log("EvenyBody")函数return了,所以把他从栈顶弹出去

2秒的定时已经完成了,所以就把对应的回调函数放到回调队列里。

事件循环检查调用栈是否为空,如果非空的话,他就等着。因为调用栈现在是空的,所以把回调队列中的回调函数丢进调用栈。

console.log("There")函数返回了,因此把他从栈顶弹出去(译者按:原文为console.log("Everybody"),应为书写错误)。

有趣的一点是:setTimeout(function(...), 0)的情况。setTimeout为0的时候这个过程看起来可能不明显,除非考虑到调用栈的执行环境和事件循环的情况。基本上都会推迟到调用栈为空才执行。

考虑UI渲染的性能的情况

为了回到了我们日常处理的UI层,我们需要考虑渲染问题。浏览器受到我们在JavaScript中所做操作的影响,他可能每隔16.6ms重绘一次屏幕(60帧/秒)。但是调用栈还有代码在执行的话,他实际上是没法做重绘的。

就像Philip说的一样:

当大家说不要"阻塞事件循环"的时候,他们实际上是说:不要把耗费时间长的代码放进调用栈,因为你要这么搞的话,浏览器就不能做他该做的事了,比如说给你搞一个漂亮流畅的UI。

Philip Roberts “What the Heck Is the Event Loop Anyway”

举个例子,滚动的处理函数触发多了会让UI变得卡顿。顺便说一句,这是我听过的对防抖最清楚的解释了,这就是你要做到的“不要阻塞事件循环”(那就是我们只在滚动处理函数被触发x次后才执行那些耗时的操作)。

结语

总之,这就是《What the heck is the event loop anyway?》的答案。Philip的演讲很好的帮我理解了什么是JavaScript,什么不是,哪个部分是运行时,哪个部分是浏览器的和我们该怎样有效的使用事件循环。好好看看这个视频吧。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101789.html

相关文章

  • []你并不知道Node

    摘要:问题什么是调用栈并且它是的一部分么调用栈当然是的一部分。为什么理解是重要的因为你在每个进程中只能获取一个调用栈。它是一个从事件队列中跳去事件的循环并且将它们的回调压入到调用栈中。当调用栈为空的时候,事件循环可以决定下一步执行哪一个。 你并不知道Node 原文:You don’t know Node 译者:neal1991 welcome to star my articles-tra...

    miqt 评论0 收藏0
  • JavaScript 工作原理之一-引擎,运行时,调用()

    摘要:本章会对语言引擎,运行时,调用栈做一个概述。调用栈只是一个单线程的编程语言,这意味着它只有一个调用栈。查看如下代码当引擎开始执行这段代码的时候,调用栈会被清空。之后,产生如下步骤调用栈中的每个入口被称为堆栈结构。 原文请查阅这里,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland。 本系列持续更新中,Github 地址请查阅这里。 这是 JavaScript 工作原...

    Betta 评论0 收藏0
  • JavaScript 工作原理之一-引擎,运行时,调用()

    摘要:本章会对语言引擎,运行时,调用栈做一个概述。调用栈只是一个单线程的编程语言,这意味着它只有一个调用栈。查看如下代码当引擎开始执行这段代码的时候,调用栈会被清空。之后,产生如下步骤调用栈中的每个入口被称为堆栈结构。 原文请查阅这里,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland。 本系列持续更新中,Github 地址请查阅这里。 这是 JavaScript 工作原...

    Alex 评论0 收藏0
  • JavaScript 如何工作:对引擎、运行时、调用概述

    摘要:调用栈是一种数据结构,它记录了我们在程序中的位置。当从这个函数返回的时候,就会将这个函数从栈顶弹出,这就是调用栈做的事情。而且这不是唯一的问题,一旦你的浏览器开始处理调用栈中的众多任务,它可能会停止响应相当长一段时间。 原文地址: https://blog.sessionstack.com... PS: 好久没写东西了,最近一直在准备写一个自己的博客,最后一些技术方向已经敲定了,又可以...

    Warren 评论0 收藏0
  • 理解异步JavaScript

    摘要:当函数结束,将会被从调用栈移出。事件循环事件循环的责任就是查看调用栈并确定调用栈是否为空。事件循环会再次检查调用栈是否为空,如果为空的话,它会把事件回调压入栈中,然后回调函数则被执行。 写在文章前 这篇文章是翻译自Sukhjinder Arora的Understanding Asynchronous JavaScript。这篇文章描述了异步和同步JavaScript是如何在运行环境中,...

    ixlei 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<