您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > 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   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === &#39;a&#39;) { b = true} else { b = false}// goodb = a === &#39;a&#39;2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Tags: Javascript  点击:(6)  评论:(0)  加入收藏
给新手朋友分享我收藏的前端必备javascript已经写好的封装好的方法函数,直接可用。方法函数总计:41个;以下给大家介绍有35个,需要整体文档的朋友私信我,1、输入一个值,将其返回数...【详细内容】
2021-12-15  Tags: Javascript  点击:(20)  评论:(0)  加入收藏
作者:一川来源:前端万有引力 1 写在前面Javascript中的apply、call、bind方法是前端代码开发中相当重要的概念,并且与this的指向密切相关。本篇文章我们将深入探讨这个关键词的...【详细内容】
2021-12-06  Tags: Javascript  点击:(19)  评论:(0)  加入收藏
概述DOM全称Document Object Model,即文档对象模型。是HTML和XML文档的编程接口,DOM将文档(HTML或XML)描绘成一个多节点构成的结构。使用JavaScript可以改变文档的结构、样式和...【详细内容】
2021-11-16  Tags: Javascript  点击:(35)  评论:(0)  加入收藏
一、判断是否IE浏览器(支持判断IE11与edge)function IEVersion() {var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串var isIE = userAgent.indexOf("comp...【详细内容】
2021-11-02  Tags: Javascript  点击:(40)  评论:(0)  加入收藏
Null、Undefined、空检查普通写法: if (username1 !== null || username1 !== undefined || username1 !== &#39;&#39;) { let username = username1; }优化后...【详细内容】
2021-10-28  Tags: Javascript  点击:(51)  评论:(0)  加入收藏
1、前言async函数,也就是我们常说的async/await,是在ES2017(ES8)引入的新特性,主要目的是为了简化使用基于Promise的API时所需的语法。async和await关键字让我们可以用一种更简...【详细内容】
2021-09-17  Tags: Javascript  点击:(61)  评论:(0)  加入收藏
为什么要使用 debugger这篇文章将介绍如何使用断点来进行 JavaScript 调试。在读这篇文章之前,需要问一个问题:为什么要使用断点来进行调试?我们首先需要认可使用断点的是必要...【详细内容】
2021-08-26  Tags: Javascript  点击:(66)  评论:(0)  加入收藏
JavaScript 可以做很多好玩的事, 从复杂的框架到处理API,有太多的东西需要学习。但是,它也能让我们只用一行就能做一些了不起的事情。1. 获得一个随机的布尔值(true/false)该函数...【详细内容】
2021-08-19  Tags: Javascript  点击:(77)  评论:(0)  加入收藏
JavaScript 提供了大量不同的处理数组的方法,这里花几分钟时间介绍 8 个项目中可以用到的数组方法。1. Array.map()使用.map() 方法,可以创建一个基于原始数组的修订版数组。....【详细内容】
2021-08-19  Tags: Javascript  点击:(95)  评论:(0)  加入收藏
▌简易百科推荐
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === &#39;a&#39;) { b = true} else { b = false}// goodb = a === &#39;a&#39;2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Mason程    Tags:JavaScript   点击:(6)  评论:(0)  加入收藏
给新手朋友分享我收藏的前端必备javascript已经写好的封装好的方法函数,直接可用。方法函数总计:41个;以下给大家介绍有35个,需要整体文档的朋友私信我,1、输入一个值,将其返回数...【详细内容】
2021-12-15  未来讲IT    Tags:JavaScript   点击:(20)  评论:(0)  加入收藏
1. 检测一个对象是不是纯对象,检测数据类型// 检测数据类型的方法封装(function () { var getProto = Object.getPrototypeOf; // 获取实列的原型对象。 var class2type =...【详细内容】
2021-12-08  前端明明    Tags:js   点击:(23)  评论:(0)  加入收藏
作者:一川来源:前端万有引力 1 写在前面Javascript中的apply、call、bind方法是前端代码开发中相当重要的概念,并且与this的指向密切相关。本篇文章我们将深入探讨这个关键词的...【详细内容】
2021-12-06  Nodejs开发    Tags:Javascript   点击:(19)  评论:(0)  加入收藏
概述DOM全称Document Object Model,即文档对象模型。是HTML和XML文档的编程接口,DOM将文档(HTML或XML)描绘成一个多节点构成的结构。使用JavaScript可以改变文档的结构、样式和...【详细内容】
2021-11-16  海人为记    Tags:DOM模型   点击:(35)  评论:(0)  加入收藏
入口函数 /*js加载完成事件*/ window.onload=function(){ console.log("页面和资源完全加载完毕"); } /*jQuery的ready函数*/ $(document).ready(function(){ co...【详细内容】
2021-11-12  codercyh的开发日记    Tags:jQuery   点击:(36)  评论:(0)  加入收藏
一、判断是否IE浏览器(支持判断IE11与edge)function IEVersion() {var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串var isIE = userAgent.indexOf("comp...【详细内容】
2021-11-02  V面包V    Tags:Javascript   点击:(40)  评论:(0)  加入收藏
Null、Undefined、空检查普通写法: if (username1 !== null || username1 !== undefined || username1 !== &#39;&#39;) { let username = username1; }优化后...【详细内容】
2021-10-28  前端掘金    Tags:JavaScript   点击:(51)  评论:(0)  加入收藏
今天我们将尝试下花 1 分钟的时间简单地了解下什么是 JS 代理对象(proxies)?我们可以这样理解,JS 代理就相当于在对象的外层加了一层拦截,在拦截方法里我们可以自定义一些个性化...【详细内容】
2021-10-18  前端达人    Tags:JS   点击:(51)  评论:(0)  加入收藏
带有多个条件的 if 语句把多个值放在一个数组中,然后调用数组的 includes 方法。// bad if (x === "abc" || x === "def" || x === "ghi" || x === "jkl") { //logic } // be...【详细内容】
2021-09-27  羲和时代    Tags:JS   点击:(58)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条