JS约瑟夫问题 循环链表还问题

JS事件循环详解 - WEB前端 - 伯乐在线
& JS事件循环详解
本文讲什么?
伴随着JavaScript这种web浏览器脚本语言的普及,对它的事件驱动交互模型,以及它与Ruby、Python和Java中常见的请求-响应模型的区别有一个基本了解,对您是有益的。在这篇文章中,我将解释一些JavaScript并发模型的核心概念,包括其事件循环和消息队列,希望能够提升你对一种语言的理解,这种语言你可能已经在使用但也许并不完全理解。
这篇文章是写给谁的?
这篇文章是针对在客户端或服务器端使用或计划使用JavaScript的web开发人员的。如果你已经精通事件循环,那么这篇文章的大部分对你来说会很熟悉。对于那些还不是很精通的人,我希望能给你提供一个基本的了解,这样可以更好地帮助你阅读和编写日常代码。
非阻塞I / O
在JavaScript中,几乎所有的I/O都是非阻塞的。这包括HTTP请求,数据库操作和磁盘读写,单线程执行要求在运行期执行一个操作时,提供一个回调函数,然后继续做其它的事情。当操作已经完成时,消息和已提供的回调函数一起插入到队列。在将来的某个时候,消息从队列移除,回调函数触发。
虽然这种交互模型可能对已经习惯使用用户界面的开发人员很熟悉,比如“mousedown,”和“click”事件在某一时刻被触发。这与通常在服务器端应用程序进行的同步式请求-响应模型是不同的。
让我们来比较一下两小块代码,发出HTTP请求到和输出响应到控制台。首先看看Ruby,配合使用Faraday(一个Ruby 的HTTP 客户端开发库):
JavaScript
response = Faraday.get ''
puts response
puts 'Done!'
response = Faraday.get ''puts responseputs 'Done!'
执行路径很容易跟踪:
执行get方法,执行的线程等待,直到收到响应
从谷歌收到响应并返回给调用者,它存储在一个变量中
变量的值(在本例中,就是我们的响应)输出到控制台
值“Done!“输出到控制台
让我们使用Node.js和Request库在JavaScript做同样的事情:
JavaScript
request('', function(error, response, body) {
console.log(body);
console.log('Done!');
request('', function(error, response, body) {&&console.log(body);});&console.log('Done!');
表面上看略有不同,实际行为截然不同:
执行请求函数,传递一个匿名函数作为回调,当响应在将来某个时候可用时执行回调。
“Done!“立即输出到控制台
在将来的某个时候,响应返回和回调执行时,输出它的内容到控制台
将调用者和响应解耦,使得JavaScript在运行期等待异步操作完成和回调触发时可以做其他事情。但是这些回调在内存中是如何组织的,按什么顺序执行?什么导致他们被调用?
JavaScript运行时包含一个消息队列,它存储了需要处理的消息的列表和相关的回调函数。这些消息是以队列的形式来响应回调函数所涉及的外部事件(如鼠标单击或收到HTTP请求的响应)的。例如,如果用户单击一个按钮,但没有提供回调函数,那么也没有消息会被加入队列。
在一次循环,队列提取下一条消息(每次提取称为一次“tick”),当事件发生,该消息的回调执行。
回调函数的调用在调用栈作为初始化frame(片段),由于JavaScript是单线程的,未来的消息提取和处理因为等待栈的所有调用返回而被停止。后续(同步)函数调用会添加新的调用frame到栈(例如,函数init调用函数changeColor)。
JavaScript
function init() {
var link = document.getElementById("foo");
link.addEventListener("click", function changeColor() {
this.style.color = "burlywood";
function init() {&&var link = document.getElementById("foo");&&&link.addEventListener("click", function changeColor() {&&&&this.style.color = "burlywood";&&});}&init();
在这个例子中,当用户单击“foo”元素时,一条消息(及其回调函数changeColor)会被插入到队列,并触发“onclick“事件。当消息离开队列时,其回调函数changeColor被调用。当changeColor返回(或者是抛出一个错误),事件循环仍在继续。只要函数changeColor存在,并指定为“foo”元素的onclick方法的回调,那么在该元素上单击会导致更多的消息(和相关的回调changeColor)插入队列。
队列附加消息
如果一个函数在代码中按异步调用(比如setTimeout),提供的回调将最终作为一个不同的消息队列的一部分被执行,它将发生在事件循环的某个未来的动作上。例如:
JavaScript
function f() {
console.log("foo");
setTimeout(g, 0);
console.log("baz");
function g() {
console.log("bar");
function h() {
console.log("blix");
12345678910111213141516
function f() {&&console.log("foo");&&setTimeout(g, 0);&&console.log("baz");&&h();}&function g() {&&console.log("bar");}&function h() {&&console.log("blix");}&f();
由于setTimeout的非阻塞特性,它的回调将在至少0毫秒后触发,而不是作为消息的一部分被处理。在这个示例中,setTimeout被调用, 传入了一个回调函数g且延时0毫秒后执行。当我们指定时间到达(当前情况是,几乎立即执行),一个单独的消息将被加入队列(g作为回调函数)。控制台打印的结果会是像这样:“foo”,“baz”,“blix”,然后是事件循环的下一个动作:“bar”。如果在同一个调用片段中,两个调用都设置为setTimeout -传递给第二个参数的值也相同-则它们的回调将按照调用顺序插入队列。
Web Workers
使用Web Workers允许您能够将一项费时的操作在一个单独的线程中执行,从而可以释放主线程去做别的事情。worker(工作线程)包括一个独立的消息队列,事件循 环,内存空间独立于实例化它的原始线程。worker和主线程之间的通信通过消息传递,看起来很像我们往常常见的传统事件代码示例。
首先,我们的worker:
JavaScript
// our worker, which does some CPU-intensive operation
var reportResult = function(e) {
pi = putePiToSpecifiedDecimals(e.data);
postMessage(pi);
onmessage = reportR
// our worker, which does some CPU-intensive operationvar reportResult = function(e) {&&pi = SomeLib.computePiToSpecifiedDecimals(e.data);&&postMessage(pi);};&onmessage = reportResult;
然后,主要的代码块在我们的HTML中以script-标签存在:
JavaScript
// 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);
// our main code, in a &script&-tag in our HTML pagevar 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入队一条消息同时带上reportResult回调函数。消息出队时,一条新消息发送回主线程,新消息入队主线程队列(带上logResult回调函数)。这样,开发人员可以将cpu密集型操作委托给一个单独的线程,使主线程解放出来继续处理消息和事件。
关于闭包的
JavaScript对闭包的支持,允许你这样注册回调函数,当回调函数执行时,保持了对他们被创建的环境的访问(即使回调的执行时创建了一个全新的调用栈)。理解我们的回调作为一个不同的消息的一部分被执行,而不是创建它的那个会很有意思。看看下面的例子:
JavaScript
function changeHeaderDeferred() {
var header = document.getElementById("header");
setTimeout(function changeHeader() {
header.style.color = "red";
changeHeaderDeferred();
12345678910111213
function changeHeaderDeferred() {&&var header = document.getElementById("header");&&&setTimeout(function changeHeader() {&&&&header.style.color = "red";&&&&&return false;&&}, 100);&&&return false;}&changeHeaderDeferred();
在这个例子中,changeHeaderDeferred函数被执行时包含了变量header。函数 setTimeout被调用,导致消息(带上changeHeader回调)被添加到消息队列,在大约100毫秒后执行。然后 changeHeaderDeferred函数返回false,结束第一个消息的处理,但header变量仍然可以通过闭包被引用,而不是被垃圾回收。当 第二个消息被处理(changeHeader函数),它保持了对在外部函数作用域中声明的header变量的访问。一旦第二个消息 (changeHeader函数)执行结束,header变量可以被垃圾回收。
JavaScript 事件驱动的交互模型不同于许多程序员习惯的请求-响应模型,但如你所见,它并不复杂。使用简单的消息队列和事件循环,JavaScript使得开发人员在构建他们的系统时使用大量asynchronously-fired(异步-触发)回调函数,让运行时环境能在等待外部事件触发的同时处理并发操作。然 而,这不过是并发的一种方法。在本文的第二部分中,我将对JavaScript的并发模型与MRI Ruby(线程和GIL),EventMachine(Ruby),Java(线程)进行比较。
更多的阅读
presentation I did recently, titled “The JavaScript Event Loop: Concurrency in the Language of the Web”
, by Aaron Stannard
关于作者:
可能感兴趣的话题
关于伯乐前端
伯乐前端分享Web前端开发,包括JavaScript,CSS和HTML5开发技术,前端相关的行业动态。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2017 伯乐在线今天看啥 热点:
js和jquery中循环的退出和继续下一个循环,jsjquery
作为水货,就是学会了1+1=3也要记录一下!错了,是2
学习记录:
for(var i=1;i&5;i++){
// 使用break,弹出2次提示分别为1,2;如果使用continue,则会弹出3次,分别是1,2,4
循环,退出循环,使用break;退出当前循环继续下一个循环,使用continue
jquery中的each()方法中要实现break,使用return false;continue,使用return true
$(...).each(function(i){//i为循环次数if(i==3)//当i==3的时候,不执行,继续下次});
1.和楼上说的那样2.用each,可以看一下each的解释会传递给回调函数元素索引和内容的,索引是0开始的,所以加1$(document).ready(function(){
$(&div[id^='tab']&).each(function(i){
$(this).mouseover(function(){
alert(i+1);
}); });});
相关搜索:
相关阅读:
相关频道:
&&&&&&&&&&&&&&&&&&
Javascript最近更新JS循环引用的内存泄露问题_JavaScript编程学院_IT学院
当前位置:&&&&
JS循环引用的内存泄露问题
如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。
简单的循环引用:
var el = document.getElementById('MyElement');
var func = function () {&}
func.element =
但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。
function init()
& & var el = document.getElementById('MyElement');
& & el.onclick = function () {&&}
init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。
下面2种方法可以解决循环引用
置空dom对象
function init()
& & var el = document.getElementById('MyElement');
& & el.onclick = function () {&&}
function init()
& & var el = document.getElementById('MyElement');
& & el.onclick = function () {&&}
将el置空,context中不包含对dom对象的引用,从而打断循环应用。 如果我们需要将dom对象返回,可以用如下方法:
function init()
& & var el = document.getElementById('MyElement');
& & el.onclick = function () {&&}
function init()
& & var el = document.getElementById('MyElement');
& & el.onclick = function () {&&}
& & try{ }
& & finally { el = }
构造新的context
使服用前:
function init()
& & var el = document.getElementById('MyElement');
& & el.onclick = function () {&&}
function elClickHandler() {&&}
function init() {
& & var el = document.getElementById('MyElement');
& & el.onclick = elClickH
init(); 把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。}

我要回帖

更多关于 数组元素循环右移问题 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信