您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

Javascript事件轮询

时间:2019-09-06 08:56:29  来源:  作者:

这篇文章关于什么?

JAVAscript作为浏览器脚本语言,已经逐渐变得无处不在,它让你对事件驱动模型有了基本理解,以及它与request-response模型的典型语言,如Ruby,Python和Java的区别,我将阐述一些关于JavaScript一致的核心概念,包括它的事件轮询和消息队列,希望能帮助你理解这门或许你不是完全理解的语言。

致读者:

这篇文章写给准备使用Javascript进行服务端/客户端开发的web开发工作者(或者准备从事该职业者),如果你对事件轮询机制有比较好的了解,这篇文章或许你会觉得很熟悉。对于不是很了解事件轮询机制的读者,我希望能帮助你对这每天读写的代码有一个基本的理解。

非阻塞I/O

在Javascript中,几乎所有的I/O都是非阻塞的,其中包括http请求,数据操作以及磁盘读写;单线程询问任务运行时机以及执行任务,通过使用回调函数,能让Javascript线程在回调完成之前执行其它任务。当一个执行完成的时候,会去执行由回调函数提供的有序消息队列中的下一个任务。

当开发者熟悉了这种交互模式,用户习惯了这种界面 — 当事件发生,例如“mousedown”,“click”这种随时可能被触发的事件,它不同于同步机制,请求-响应模型很少用在服务端应用上。

让我们比较两块代码,它们分别发起HTTP请求于www.google.com然后在控制台输出响应结果。首先,Ruby,使用Faraday:

response = Faraday.get 'http://www.google.com'
puts response
puts 'Done!'

执行结果如下所示:

get方法已执行,然而该线程直到有响应才被回收

该来自Google的响应返回的数据并没有存在变量中

响应结果输出在控制台中

直至最后,“任务完成”才出现在控制台中

让我们用Javascript的Node.js及它的Request库来做同样的事:

request('http://www.google.com', function(error, response, body) {
 console.log(body);
});
console.log('Done!');

请求已被执行,在请求得到响应之前则已跳过一个匿名回调函数(该函数并未执行)

“任务完成”马上出现在控制台中

一段时间之后,收到响应,此时回调函数才被执行—在控制台输出响应结果

事件轮询

非耦合机制使得Javascript线程能在等待异步操作完成及其回调函数执行之前执行其它任务。那么,在内存中在哪激活回调?回调按什么规则执行?什么会让回调执行呢?

Javascript线程包括一个储存了待执行任务的消息列表的消息队列,以及与它们相关联的回调函数,这些消息按照它们的响应顺序排列(例如鼠标点击,或者收到来自HTTP请求的响应)每条消息都有回调函数,如果没有提供回调函数:例如当用户点击一个按钮但是没有提供回调函数,则没有消息会被添加到消息队列。

在每一次轮询中,任务队列会记录下一条消息(每次记录会返回一个“tick”),当轮询到这条消息时,该消息所对应的回调函数则被执行。

Javascript事件轮询

 

在最初的架构中,回调函数通过调用栈来实现,由于Javascript是单线程的,消息队列是阻塞的,对于后续任务,必须等待之前的任务返回栈中所有回调函数,才能将新任务的回调函数加入到栈中。在随后的架构中加入了函数(同步的)对栈的新的调用方法(此处例举一个初始化为changeColor的函数)。

function init() {
 var link = document.getElementById("foo");
 link.addEventListener("click", function changeColor() {
 this.style.color = "burlywood";
 });
}
init();

在这个例子中,当用户点击“foo”这个元素,“onclick”事件被触发,一个消息(以及回调函数changeColor)加入消息队列。当队列按序执行到该消息时,它的回调函数changeColor被唤起。

当回调函数changeColor返回(或者出错被丢掉),事件轮询继续执行。只要与”foo”元素的onclick事件绑定的回调函数changeColor存在,随后在该元素上的click事件都会使得更多的消息(及其回调函数changeColor)被加入队列。

消息队列的添加

如果你申明了一个异步函数(例如setTimeout),其回调函数最终会在一个不同的消息队列中执行,在未来的事件轮询中的某个时刻。例如:

function f() {
 console.log("foo");
 setTimeout(g, 0);
 console.log("baz");
 h();
}
function g() {
 console.log("bar");
}
function h() {
 console.log("blix");
}
f();

由于setTimeout非阻塞的本质,它的回调函数的未来的若干毫秒后执行并且等待期间不占用该消息的进程。在这个例子中,setTimeout跳过它的回调函数g和一段事件的延迟后被唤起。当预先声明的时间结束(在这个例子中几乎是立即执行)被分离出去的消息又被重新加回队列,包括其回调函数g。这个回调函数被激活就好比:”foo”,”baz”,”blix”然后执行下一个事件轮询的tick:”bar”。如果在一个框架中同时声明了两个setTimeout,并且他们的第二个参数(执行时间)想同,他们的回调将会按照其定义顺序执行。

Web Workers

利用Web Workers能让你丢掉昂贵的多线程执行方式,释放主线程去做其他的事。Web Workers包括单独的消息队列,事件轮询,以及实例化了一个独立于最初的主线程的储存空间。利用消息传递来建立消息与主线程之间的联系,这种联系非常像我们刚才的代码示例。

Javascript事件轮询

 

首先,我们的worker:

// our worker, which does some CPU-intensive operation
var reportResult = function(e) {
 pi = SomeLib.computePiToSpecifiedDecimals(e.data);
 postMessage(pi);
};
onmessage = reportResult;

然后,这是在html中的代码内容:

// our main code, in a <script>-tag in our HTML page
var piWorker = new Worker("pi_calculator.js");
var logResult = function(e) {
 console.log("PI: " + e.data);
};
piWorker.addEventListener("message", logResult, false);
piWorker.postMessage(100000);

该示例中,主线程产生一个worker然后将一个logResult回调函数注册到消息队列中。在worker中,reportResult函数被注册到它自己的消息事件中。当worker线程从主线程接收消息时,worker将消息及其相应的回调函数加入队列中。当消息队列按顺序执行到该消息时,主线程将发回一条消息并将一条新的消息加入队列(按照logResult的回调排序)由此开发者能让CPU集中处理分线程,释放主线程继续处理消息任务及其绑定事件。

关于闭包

Javascript支持闭包,准许注册回调,当我们执行回调时,通过执行回调创造的新的完全调用栈来维持我们创造的环境的入口。回调函数作为不同于我们创造的消息的一部分被调用。考虑如下示例:

function changeHeaderDeferred() {
 var header = document.getElementById("header");
 
 setTimeout(function changeHeader() {
 header.style.color = "red";
 return false;
 }, 100);
 return false;
}
changeHeaderDeferred();

在这个示例中,以头变量方式声明的changeHeaderDeferred函数被执行。setTimeout函数被唤醒,导致消息(加在changeHeader回调中的)大约在100毫秒之后(时间偏差源于每台计算机内置原子钟差异)添加到消息队列,changeHeaderDeferred返回false,结束第一条消息的进程,然而头变量依然通过闭包的方式存在,没有被垃圾回收机制回收。当第二条消息执行(changeHeader函数)维持头变量声明的外部函数域的入口。一旦第二条消息(changeHeader函数)执行完毕,头变量则被回收。

另外

Javascript的事件驱动交互模型不同于大多数编程人员习惯的请求-响应模型,但是你能看到,该技术也不是那么高不可攀。一个简单的消息队列及事件轮询,Javascript使得开发者能够围绕收集异步回调的形式来建立他们的系统,在等待外部事件发生的同时释放主线程去做其它操作。它将越来越流行。

希望本文能帮助到您!

点赞+转发,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓-_-)

关注 {我},享受文章首发体验!

每周重点攻克一个前端技术难点。更多精彩前端内容私信 我 回复“教程”

原文链接:http://eux.baidu.com/blog/fe/javascript-loop

作者:erin



Tags:Javascript   点击:()  评论:()
声明:本站部分内容来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除,谢谢。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
众所周知,JavaScript 一直在快速变化。在新的 ES2020 中,有很多很棒的特性,我们大都已经迫不及待尝试了。老实说,有时我们可以用不同角度来编写代码,同样也能达到相同的效果,而且...【详细内容】
2020-07-03   Javascript  点击:(0)  评论:(0)  加入收藏
在这篇文章中,我将向您展示如何通过JavaScript在网页上访问设备的摄像头,并支持多种浏览器,而无需外部库。我不好看,好看的是文字如何使用相机要访问用户的相机(或麦克风),我们使用...【详细内容】
2020-06-30   Javascript  点击:(2)  评论:(0)  加入收藏
用任何编程语言来开发程序,都是为了让计算机干活,比如编写一篇文章,下载一首MP3等,而计算机干活的CPU只认识机器的指令,所以,尽管不同的编程语言差异极大,最后都得“翻译”成CPU可...【详细内容】
2020-06-29   Javascript  点击:(1)  评论:(0)  加入收藏
JavaScript 语言中的 for 循环用于多次执行代码块,它是 JavaScript 中最常用的一个循环工具,还可用于数组的遍历循环等。我们为什么要使用 for 循环呢?打个比方,例如我们想要...【详细内容】
2020-06-28   Javascript  点击:(3)  评论:(0)  加入收藏
在 JS 没有提供一种简便的方法来替换所有指定字符。 在 Java 中有一个 replaceAll() ,replaceAll(String regex, String replacement))方法使用给定的参数 replacement 替换...【详细内容】
2020-06-24   Javascript  点击:(0)  评论:(0)  加入收藏
Cleave,Voca,Slick,Tensorflow等> Photo by Patrick Hendry on Unsplash JavaScript从一开始就得到了发展。 毫无疑问,JS是最受欢迎的语言之一,它可能会保持这种状态。 流行的库...【详细内容】
2020-06-23   Javascript  点击:(0)  评论:(0)  加入收藏
书上用了一个阶乘功能来演示递归:7.1 递归(阶乘)function factorial(number){ if (number <= 1){ return 1; }else { return number * arguments.calle...【详细内容】
2020-06-23   Javascript  点击:(0)  评论:(0)  加入收藏
this的工作原理如果一个函数被作为一个对象的方法调用,那么this将被指派为这个对象。var parent = { method: function () { console.log(this); }};parent.met...【详细内容】
2020-06-19   Javascript  点击:(0)  评论:(0)  加入收藏
今天我们来聊聊js引擎JavaScript很酷,但是机器如何才能真正理解您编写的代码?作为JavaScript开发人员,我们通常不必自己处理编译器。但是,一定要了解JavaScript引擎的基础知识,看...【详细内容】
2020-06-18   Javascript  点击:(1)  评论:(0)  加入收藏
JavaScript是一种发展迅速的语言。这篇文章,我想展示一些有关如何在JavaScript中应用函数式编程的示例。JavaScript中的函数式编程即使函数式编程可以极大地改善应用程序的代...【详细内容】
2020-06-09   Javascript  点击:(0)  评论:(0)  加入收藏
一般情况下,忽视内存管理不会对传统的网页产生显著的后果。这是因为,用户刷新页面后,内存数据都被清理了。...【详细内容】
2020-06-08   Javascript  点击:(2)  评论:(0)  加入收藏
在过去的几年中,已经将许多有用的功能添加到Javascript Array全局对象中,这些功能为开发人员在编写可用于数组的代码时提供了多种选择。这些功能提供了许多优点,其中最值得注意...【详细内容】
2020-06-07   Javascript  点击:(3)  评论:(0)  加入收藏
对象冻结这是使用Object.freeze的方法:let objectToFreeze = { age: 28, name: "Damien", pets: ["Symba", "Hades", "Kiwi"], sibling: { age: 25, name: "Core...【详细内容】
2020-06-05   Javascript  点击:(2)  评论:(0)  加入收藏
1、anchor创建一个锚点。document.write(&#39;hello&#39;.anchor(&#39;anchor&#39;))// 输出:<a name="anchor">hello</a>2、big创建一个加粗的文本 big 标签包裹。document...【详细内容】
2020-06-05   Javascript  点击:(0)  评论:(0)  加入收藏
作为JavaScript开发人员,我们可以挑战自己成长的一个方法就是用刷题来练习!下面的问题旨在具有挑战性和启发性。如果你确切地知道如何回答每一个问题,那很好,但如果你答错了,并...【详细内容】
2020-06-01   Javascript  点击:(2)  评论:(0)  加入收藏
因为工作中经常用到这些方法,所有便把这些方法进行了总结。JavaScript1. type 类型判断isString (o) { //是否字符串 return Object.prototype.toString.call(o).slice(8,...【详细内容】
2020-05-31   Javascript  点击:(4)  评论:(0)  加入收藏
继承 js中的继承一般分为三部分:原型属性继承、静态属性继承、实例属性继承,一个原型上面定义的方法一般都是基于其实例的用途来定义的,也就是说,原型的方法应该是实例经常用...【详细内容】
2020-05-22   Javascript  点击:(3)  评论:(0)  加入收藏
作者: 李松峰转发链接:https://mp.weixin.qq.com/s/guAN1Cz2gYfKdBhmUpLyVA前言JavaScript这门语言的第一个演示版差不多就在25年前诞生。 没记错的话,25年前的今天,1995年5月...【详细内容】
2020-05-20   Javascript  点击:(5)  评论:(0)  加入收藏
出于安全和隐私的原因,web 应用程序不能直接访问用户设备上的文件。如果需要读取一个或多个本地文件,可以通过使用input file和FileReader来实现。在这篇文章中,我们将通过一些...【详细内容】
2020-05-20   Javascript  点击:(1)  评论:(0)  加入收藏
出于安全和隐私的原因,web 应用程序不能直接访问用户设备上的文件。如果需要读取一个或多个本地文件,可以通过使用input file和FileReader来实现。在这篇文章中,我们将通过一些...【详细内容】
2020-05-20   Javascript  点击:(0)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条