Javascript的一个优点是它如何处理异步代码。异步代码被放置到事件队列中,直到执行所有其他代码,而不会阻塞线程。但是,初学者编写异步代码可能很困难。在这篇文章中,我将消除您可能有的混乱。

了解异步代码

Javascript最基本的异步函数setTimeout和setinterval.settimeout执行一个给定的函数一定时间后,它接受一个回调函数作为第一个参数,一个毫秒的时间作为第二个参数。以下是使用的例子:






console.log();
setTimeout(){()函数(
console.log(C)
},500);
setTimeout(){()函数(
console.log(D)
},500);
setTimeout(){()函数(
console.log()
},500);
console.log(B);



正如预期的那样,控制台输出,B,约500毫秒,然后C,D,和我使用它因为setTimeout实际上是不可预测的。事实上,即使是HTML5规范提到这个问题:

这个API并不能保证定时会准时准确地运行。CPU负载、其他任务等引起的延迟是可以预测的。




有趣的是,超时不到所有其它的代码在同一程序段执行的发生。所以如果超时设置和执行一个函数需要运行很长时间,超时未到执行的函数的开始。事实上,异步函数,如setTimeout和setInterval,压入队列称为事件循环。

事件循环是一个回调函数队列。当异步函数执行时,回调函数压入队列。Javascript引擎无法处理事件循环直到执行异步功能。这意味着Javascript代码不是多线程的,即使行为是相似的。事件循环是一种先进的先进先出(FIFO)队列,这表明回调将它们添加到queue.javascript顺序执行的选择节点作为开发语言,因为它是如此的简单,写这样的代码。

Ajax

异步Javascript和XML(AJAX)永久地改变了Javascript语言的状态。突然,浏览器不再需要重新加载更新网页。在不同的浏览器AJAX代码的实现可能是漫长而无聊的。但是多亏了jQuery(以及其他库)的帮助,我们可以以简单而优雅的方式实现客户机与服务器的通信。

我们可以很容易地使用jQuery跨浏览器接口Ajax检索数据,但它不能显示幕后发生的事情:


变量数据;
$ ajax({
URL1
成功:函数(数据){
这将 /但是!
console.log(数据);
}
})
这不会工作 / /哎呀…
console.log(数据);




更容易犯的错误是在调用Ajax之后立即使用数据,但事实上它是:






Xmlhttp.open(
xmlhttp.onreadystatechange =功能(数据){
如果(xmlhttp.readystate = 4){
console.log(数据);
}
};
Xmlhttp.send(空);



底层的XMLHttpRequest对象发出请求,设置回调函数来处理XHR的readystatechnage事件。然后发送方法的XHR执行。在XHR操作,当属性发生变化,readystatechange事件将被触发。只有当XHR接收从远程服务器的响应,将触发执行回调函数。

处理异步代码

异步编程是容易堕地狱,我们常说的回调。因为事实上,几乎所有的JS异步函数使用回调。连续执行几个异步函数的结果是嵌套回调函数和随之而来的复杂代码。

在Node.js的功能很多也都是异步的。所以下面的代码基本上是共同的:






var =要求(FS );
fs.exists(指数。JS
fs.readfile(指数。JS
内容=功能(内容); / /内容做些什么
fs.writefile(指数。JS
console.log(唷!最后……;
});
});
});
console.log(执行…);



下面的客户端代码在很多方面也有:






GMaps.geocode({
地址:FromAddress,
回调函数(结果,状态){
如果(状态= ok){
fromlatlng =结果{ 0 }。geometry.location;
GMaps.geocode({
地址:地址,
回调函数(结果,状态){
如果(状态= ok){
tolatlng =结果{ 0 }。geometry.location;
map.getroutes({
来源:{ fromlatlng.lat()()()},fromlatlng.lng,
目的地:{ tolatlng.lat()()()},tolatlng.lng,
TravelMode:驾驶
单位制:帝国
回调函数(e){
console.log(annnnd最后这里的方向…);
某事与e /做
}
});
}
}
});
}
}
});



嵌套回调可以真的急了,但有几个解决方案

嵌套的回调很容易导致代码的坏味道,但你可以尝试以下方式解决这个问题

问题不在于语言本身,而在于程序员的方式。


没有不好的语言,只有不好的程序猿-异步javascript



命名函数

清除嵌套回调一个方便的解决方案是简单地避免双层嵌套。通过指定回调函数的参数,而不是通过一个匿名函数:






无功fromlatlng,tolatlng;
无功routedone =功能(e){
console.log(annnnd最后这里的方向…);
某事与e /做
};
无功toaddressdone =函数(结果、状态){
如果(状态= ok){
tolatlng =结果{ 0 }。geometry.location;
map.getroutes({
来源:{ fromlatlng.lat()()()},fromlatlng.lng,
目的地:{ tolatlng.lat()()()},tolatlng.lng,
TravelMode:驾驶
单位制:帝国
routedone回调:
});
}
};
无功fromaddressdone =函数(结果、状态){
如果(状态= ok){
fromlatlng =结果{ 0 }。geometry.location;
GMaps.geocode({
地址:地址,
toaddressdone回调:
});
}
};
GMaps.geocode({
地址:FromAddress,
fromaddressdone回调:
});



此外,该async.js库可以帮助我们处理多个Ajax请求/响应,等:






Async.parallel({
函数(完成){
GMaps.geocode({
地址:地址,
回调函数(结果){
完成(空,结果);
}
});
},
函数(完成){
GMaps.geocode({
地址:FromAddress,
回调函数(结果){
完成(空,结果);
}
});
}
}函数(错误,结果){
getroute(结果{ 0 },结果{ 1 });
});



这个代码执行两个异步函数,每个函数接收一个名为完成的回调函数,并在函数的结尾调用它。当两个完成回调函数完成时,调用并行函数的回调函数,执行或处理由两个异步函数产生的结果或错误。

承诺模型

它是从CommonJS 介绍/:

承诺表示一个操作在独立操作之后返回的最终结果。


有许多库包含的承诺模型,在jQuery已经有用的和优秀的承诺api.jquery介绍版本1.5的延迟对象可以使用jQuery的结果。延期的建设在函数返回的承诺,回报承诺的功能是用来执行一些异步操作完成后,要解决延迟。






VaR的地理编码=功能(地址){
VaR的定义新的延期()美元;
GMaps.geocode({
地址:地址,
回调函数(响应,状态){
返回dfd.resolve(响应);
}
});
返回dfd.promise();
};
无功getroute =功能(fromlatlng,tolatlng){
VaR的定义新的延期()美元;
map.getroutes({
来源:{ fromlatlng.lat()()()},fromlatlng.lng,
目的地:{ tolatlng.lat()()()},tolatlng.lng,
TravelMode:驾驶
单位制:帝国
回调函数(e){
返回dfd.resolve(E);
}
});
返回dfd.promise();
};
无功dosomethingcoolwithdirections =功能(路由){
具有路由 /做的事情
};
美元。当(地理编码(FromAddress),地理编码(解决))。
然后(功能(fromlatlng,tolatlng){
getroute(fromlatlng,tolatlng)。然后(dosomethingcoolwithdirections);
});



这允许您执行两个异步函数,等待它们的结果,然后执行前两个调用的结果的另一个函数。

承诺表示一个操作在独立操作之后返回的最终结果。


在这段代码中,地理编码方法执行两次并返回一个承诺。执行异步功能,并解决在其回调调用。然后,一个电话是通过解决,然后将被执行,它接收到的地理编码两次之前调用的返回结果。结果然后进入getroute,这个方法返回一个承诺。最后,当getroute承诺解决的dosomethingcoolwithdirections回调执行。



事件

事件的另一种方式是沟通时的异步回调完成。对象可以是一个发射器发送一个事件,而其他听的事件。这类事件的处理被称为观察者模式,backbone.js图书馆创建withbackbone.events等功能模块。






无功somemodel = backbone.model.extend({
网址: / someurl
});
无功集中= backbone.view.extend({
初始化:函数(){
This.model.on(复位
This.model.fetch();
},
渲染:函数(数据){
用数据做某事
}
});
VaR视角=新的集中({
型号:新somemodel()
});



还有其他的例子,功能混合发射事件库,比如jQuery事件源,monologue.js EventEmitter,内置模块,Node.js EventEmitter。

事件循环是回调函数的一个队列。


类似的方式发送消息称为中介模式,这是用于postal.js图书馆。在中介模式,有一个为所有对象听调度事件的中间人。在这种模式下,一个对象与其他对象没有直接联系,使物体彼此分离。

绝不返回对通用API的承诺,这不仅与API用户对承诺的使用有关,而且使重构更加困难。然而,内部使用的承诺和外部接口事件的结合可以使应用程序不那么耦合,更容易测试。

在前面的例子中,该dosomethingcoolwithdirections回调函数是在两地理编码功能完成后执行。然后,将得到的dosomethingcoolwithdirections响应从getroute接收和发送它作为一个消息。






无功dosomethingcoolwithdirections =功能(路由){
Postal.channel(UI)。出版(方向。做),{
路线:路线
});
};



这允许对异步回调响应没有请求的对象直接引用的应用程序的其他部分。当你得到命令,很可能是很多的页面需要更新。在一个典型的AJAX程序,平稳回调可能要做相应的调整,当接收到的命令更改。这可能会使代码难以维护,但通过使用信息,它将处理UI的多个区域的更新更容易。






函数(){()
this.channel = postal.channel(UI);
this.channel.subscribe(做方向。
};
ui.prototype.updatedirections =功能(数据){
路线可在 / data.route现在更新UI
};
app.ui =新的UI();



其他的图书馆,将基于中介模式的信息放大,pubsubjs,和radio.js。

结论

Javascript代码非常容易使用。编写异步承诺、事件或命名函数以避免回调地狱。更多信息请点击Javascript异步编程,异步Javascript:少构建快速响应的应用程序。更多的例子,托管在GitHub上,nettutsasyncjs地址,克隆快!