龙空技术网

几张图告诉你事件循环和Job Queue究竟是什么

海外华文客户端 116

前言:

目前兄弟们对“jqueryqueueinitque”大概比较珍视,咱们都想要剖析一些“jqueryqueueinitque”的相关资讯。那么小编也在网摘上网罗了一些关于“jqueryqueueinitque””的相关文章,希望同学们能喜欢,小伙伴们一起来了解一下吧!

由于原文篇幅过长,先放出文章的前面一部分,专门讲解事件循环和Job Queue。文中用简洁明了的配图清晰地解释了事件循环的机制,希望大家能有所收获~

本系列专门讨论JavaScript及其核心组件,本文属于该系列的第4部分。在识别和描述核心元素的过程中,我们还分享了构建SessionStack的一些经验法则,像SessionStack这样的JavaScript应用必须保持强大和高性能,才能保持竞争力。

这一次,我们将回顾在单线程环境中编程的缺点,以及如何克服它们来构建令人惊叹的JavaScript UI。

为什么单线程会限制我们?

在 第一篇文章中,我们思考了一个问题,当调用堆栈(Call Stack)中的函数需要花费大量的处理时间时,会发生什么。

例如,想象一下在浏览器中运行复杂的图像转换算法.

虽然调用堆栈有待执行的功能,但是浏览器不能做其他任何事情—它被阻塞了。这意味着浏览器不能渲染,它不能运行任何其他的代码,它就这样卡住了。问题来了,应用的界面不再高效和美观了。

你的应用 被卡住了.

有时候,这可能不是一个严重的问题。但是,它将会带来更大的问题。一旦你的浏览器开始在调用栈中处理太多的任务,它可能会在很长一段时间内停止响应。在这一点上,许多浏览器会抛出错误来询问是否应该终止页面:

它很丑,它完全毁了你的用户体验:

JavaScript程序的构建块

也许你只是在一个js文件中写了一个JavaScript应用,但是您的程序几乎肯定是由几个块组成的,现在只有一个将要执行,其余的将在以后执行。最常用的块单元是函数。

JavaScript开发新手最常见的问题是不理解以后的事情并不一定会在当前任务完成后立即发生。即,从定义上来说,现在无法完成的任务,将会异步完成,这意味着不会发生上述的阻塞行为。

让我们来看看下面的例子:

// ajax(..) is some arbitrary Ajax function given by a libraryvar response = ajax('');console.log(response);// `response` won't have the response

您可能已经意识到标准的Ajax请求不会同步完成,这意味着在代码执行的时候Ajax(..)函数没法给response变量赋予真正的返回值。

“等待”异步函数返回其结果的一个简单方法是使用一个名为回调的函数:

ajax('', function(response) { console.log(response); // `response` is now available});

请注意:实际上可以实现同步Ajax请求。但永远不要这样做。如果进行同步Ajax请求,JavaScript应用的界面将会被阻塞——用户将无法单击、输入数据、导航或滚动。这将防止任何用户交互。这是一种糟糕的做法。

这就是它的样子,但请千万不要这样做——不要破坏网络:

// This is assuming that you're using jQueryjQuery.ajax({ url: '', success: function(response) { // This is your callback. }, async: false // And this is a terrible idea});

我们使用Ajax请求只是为了示范异步。你可以让任意代码块异步执行。

setTimeout(callback, milliseconds) 方法也可以实现异步。setTimeout函数所做的是设置一个事件(超时),以便稍后发生。让我们来看看:

function first() { console.log('first');}function second() { console.log('second');}function third() { console.log('third');}first();setTimeout(second, 1000); // Invoke `second` after 1000msthird();

控制台的输出如下:

firstthirdsecond

解析事件循环

事先声明—尽管JavaScript允许异步的代码(如我们刚刚讨论的setTimeout),在ES6之前,JavaScript本身实际上从未有任何关于异步的直接概念。JavaScript引擎只会在任意给定的时刻执行一个代码块。

有关JavaScript引擎如何工作的更多细节(特别是Google的V8引擎),请看我们 前面的文章。

那么,谁告诉JS引擎来执行你的代码块呢?实际上,JS引擎并不是单独运行的——它运行在一个托管环境中,对于大多数开发人员来说,就是典型的Web浏览器或Node.js。实际上,如今JavaScript已经嵌入到各种设备中,从机器人到电灯泡。每一个设备代表了JS引擎的不同类型的托管环境。

所有环境的共同特点是具有一个称为事件循环的内置机制,它可以处理程序的多个块的执行,每次执行都调用JS引擎。

这意味着,JS引擎只是任意JS代码的按需执行环境。它是安排事件(JS代码执行)的周边环境。

所以,当您的JavaScript程序发出Ajax请求从服务器获取一些数据时,你在一个函数(“callback”)中设置了“response”代码,而JS引擎告诉托管环境:“嘿,我现在要暂停执行了,但是无论何时你完成了这个网络请求,拿到数据后,请继续调用这个函数。”

然后浏览器设置侦听来自网络的响应,当它有什么东西返回给你的时候,它将通过将它插入到事件循环中来调度回调函数。

让我们看下下图:

你可以在 前一篇文章中了解更多关于内存堆和调用堆栈的信息。

图中这些Web API是什么? 本质上,它们是您无法访问的线程,你只能对它们进行调用。它们是并行启动的浏览器的一部分。如果你是一个 Node.jS开发者,这些是C++ API。

那么,事件循环到底是什么呢?

事件循环有一个简单的工作——监视调用堆栈和回调队列。如果调用堆栈是空的,它将从队列中取出第一个事件,并将其推到调用堆栈,该堆栈有效地运行它。

这样的迭代被称为事件循环中的一个tick 。每个事件都只是一个函数回调。

console.log('Hi');setTimeout(function cb1() { console.log('cb1');}, 5000);console.log('Bye');

让我们“执行”这段代码,看看会发生什么:

1. 状态很干净。浏览器控制台显示为空,调用堆栈为空。

2. console.log('Hi') 被添加到调用堆栈中。

3. console.log('Hi') 被执行。

4. console.log('Hi') 从调用堆栈中被删除。

5. setTimeout(function cb1() { ... }) 被添加到调用堆栈中.

6. setTimeout(function cb1() { ... }) 被执行。作为Web API的一部分,浏览器创建了一个计时器。它将为你处理倒计时。

7. setTimeout(function cb1() { ... }) 本身已执行完,从调用堆栈中被删除。

8. console.log('Bye') 被添加到调用堆栈中。

9. console.log('Bye') 被执行。

10. console.log('Bye')从调用堆栈中被删除。

11. 在至少 5000 ms 后, 计时器完成并将cb1回调推到回调队列。

12. 事件循环从回调队列中获取cb1,并将其推到调用堆栈。

13. cb1 被执行,将 console.log('cb1')添加到调用堆栈。

14. console.log('cb1') 被执行。

15. console.log('cb1') 从调用堆栈中被删除。

16. cb1 从调用堆栈中被删除。

一个快速回顾:

值得注意的是,ES6指定了事件循环应该如何工作,这意味着从技术上讲,它被加入到JS引擎的职责范围内,不再只是托管环境中的一部分。造成这种变化的一个主要原因是在ES6中引入了Promise,因为后者需要对事件循环队列的调度操作进行直接的、细粒度的控制(稍后我们将详细讨论它们)。

setTimeout(…) 是怎么样工作的

需要注意的是setTimeout(…)不会自动将您的回调放到事件循环队列中。它设置了一个计时器。当计时器过期时,托管环境将您的回调放入事件循环中,以便将来的某个时间点将会接收并执行它。看看这段代码:

setTimeout(myCallback, 1000);

这并不意味着myCallback 将在1000毫秒时执行,而是在1000毫秒时,myCallback将被添加到队列中。不过,队列可能会有其他之前添加的事件——你的回调将不得不等待。

相当多教你开始使用异步JavaScript代码的文章和教程会建议你用setTimeout(callback, 0)。现在你知道事件循环做了什么事情,以及setTimeout是如何工作的:调用setTimeout,并将0作为第二个参数只是推迟回调,直到调用堆栈为空。

看看下面的代码:

console.log('Hi');setTimeout(function() { console.log('callback');}, 0);console.log('Bye');

尽管等待时间设置为0毫秒,但浏览器控制台的结果如下:

HiByecallback

ES6中的作业(Job)是什么 ?

在ES6中引入了一个称为“作业队列(Job Queue)”的新概念。它是事件循环队列之上的一个层。在处理Promise的异步行为时,您很可能会遇到这种情况(我们也会讨论它们)。

我们现在只讨论这个概念,这样当我们讨论异步行为时,就会明白这些行为是如何被调度和处理的。

想象一下这样的情况:作业队列是一个队列,它连接到事件循环队列的每一个tick的末尾。在事件循环期间可能发生的某些异步操作不会导致将整个新事件添加到事件循环队列中,而是会将一个项目(也就是Job)添加到当前tick的作业队列的末尾。

这意味着你可以添加一个稍后执行的功能,并且能保证它将在其他任何事情之前执行。

作业还可以在同一队列的末尾添加更多的作业。从理论上讲,一个作业“循环”(一个不断增加其他作业的作业,等等)可能会无限期地运行,这样就会消耗程序必要的资源,无法进行下一个事件循环。在概念上,这类似于在你的代码写了一个长时间运行代码的或无限循环(就像while (true))。

作业有点像 setTimeout(callback, 0) 的“hack”,但是作业实现了这样一种方式,他们引入了一个更明确、更有保证的顺序:稍后执行,但是尽可能快地执行。

(未完下篇继续分析)

免责声明:转载自网络 不用于商业宣传 版权归原作者所有 侵权删

标签: #jqueryqueueinitque