异步轮询机制,js异步机制

访客3年前黑客资讯341

这篇文章主要介绍了深入理解JavaScript编程中的同步与异步机制,不仅仅是AJAX已经深入到了各个角落,Node.js的火爆也让 *** 的异步编程格外引人注目,需要的朋友可以参考下  JavaScript的优势之一是其如何处理异步代码。

异步代码会被放入一个事件队列,等到所有其他代码执行后才进行,而不会阻塞线程。

然而,对于初学者来说,书写异步代码可能会比较困难。

而在这篇文章里,我将会消除你可能会有的任何困惑。

  理解异步代码  JavaScript最基础的异步函数是setTimeout和setInterval。

setTimeout会在一定时间后执行给定的函数。

它接受一个回调函数作为之一参数和一个毫秒时间作为第二参数。

以下是用法举例:  ? 1 2 3 4 5 6 7 8 9 10 11 console.log( a ); setTimeout(function() { console.log( c ) }, 500 ); setTimeout(function() { console.log( d ) }, 500 ); setTimeout(function() { console.log( e ) }, 500 ); console.log( b );   正如预期,控制台先输出a、b,大约500毫秒后,再看到c、d、e。

我用大约是因为setTimeout事实上是不可预知的。

实际上,甚至 HTML5规范都提到了这个问题:  这个API不能保证计时会如期准确地运行。

由于CPU负载、其他任务等所导致的延迟是可以预料到的。

  有趣的是,直到在同一程序段中所有其余的代码执行结束后,超时才会发生。

所以如果设置了超时,同时执行了需长时间运行的函数,那么在该函数执行完成之前,超时甚至都不会启动。

实际上,异步函数,如setTimeout和setInterval,被压入了称之为Event Loop的队列。

  Event Loop是一个回调函数队列。

当异步函数执行时,回调函数会被压入这个队列。

JavaScript引擎直到异步函数执行完成后,才会开始处理事件循环。

这意味着JavaScript代码不是多线程的,即使表现的行为相似。

事件循环是一个先进先出(FIFO)队列,这说明回调是按照它们被加入队列的顺序执行的。

JavaScript被 node选做为开发语言,就是因为写这样的代码多么简单啊。

  Ajax  异步Javascript与XML(AJAX)永久性的改变了Javascript语言的状况。

突然间,浏览器不再需要重新加载即可更新web页面。

在不同的浏览器中实现Ajax的代码可能漫长并且乏味;但是,幸亏有jQuery(还有其他库)的帮助,我们能够以很容易并且优雅的方式实现客户端-服务器端通讯。

  我们可以使用jQuery跨浏览器接口$.ajax很容易地检索数据,然而却不能呈现幕后发生了什么。

比如:  ? 1 2 3 4 5 6 7 8 9 10 var data; $.ajax({ url: some/url/1, success: function( data ) { // But, this will! console.log( data ); } }) // Oops, this won't work... console.log( data );   较容易犯的错误,是在调用$.ajax之后马上使用data,但是实际上是这样的:  ? 1 2 3 4 5 6 7 xmlhttp.open( GET, some/ur/1, true ); xmlhttp.onreadystatechange = function( data ) { if ( xmlhttp.readyState === 4 ) { console.log( data ); } }; xmlhttp.send( null );   底层的XmlHttpRequest对象发起请求,设置回调函数用来处理XHR的readystatechnage事件。

然后执行XHR的send *** 。

在XHR运行中,当其属性readyState改变时readystatechange事件就会被触发,只有在XHR从远端服务器接收响应结束时回调函数才会触发执行。

  处理异步代码  异步编程很容易陷入我们常说的回调地狱。

因为事实上几乎 *** 中的所有异步函数都用到了回调,连续执行几个异步函数的结果就是层层嵌套的回调函数以及随之而来的复杂代码。

  node.js中的许多函数也是异步的。

因此如下的代码基本上很常见:  ? 1 2 3 4 5 6 7 8 9 10 var fs = require( fs ); fs.exists( index.js, function() { fs.readFile( index.js, utf8, function( err, contents ) { contents = someFunction( contents ); // do something with contents fs.writeFile( index.js, utf8, function() { console.log( whew! Done finally... ); }); }); }); console.log( executing... );   下面的客户端代码也很多见:  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 GMaps.geocode({ address: fromAddress, callback: function( results, status ) { if ( status == OK ) { fromLatLng = results[0].geometry.location; GMaps.geocode({ address: toAddress, callback: function( results, status ) { if ( status == OK ) { toLatLng = results[0].geometry.location; map.getRoutes({ origin: [ fromLatLng.lat(), fromLatLng.lng() ], destination: [ toLatLng.lat(), toLatLng.lng() ], travelMode: driving, unitSystem: imperial, callback: function( e ){ console.log( ANNNND FINAL *** here's the directions... ); // do something with e } }); } } }); } } });   Nested callbacks can get really nasty, but there are several solutions to this style of coding.  嵌套的回调很容易带来代码中的坏味道,不过你可以用以下的几种风格来尝试解决这个问题  The problem isn't with the language itself; it's with the way programmers use the language Async Javascript.  没有糟糕的语言,只有糟糕的程序猿 异步JavaSript  命名函数  清除嵌套回调的一个便捷的解决方案是简单的避免双层以上的嵌套。

传递一个命名函数给作为回调参数,而不是传递匿名函数:  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var fromLatLng, toLatLng; var routeDone = function( e ){ console.log( ANNNND FINAL *** here's the directions... ); // do something with e }; var toAddressDone = function( results, status ) { if ( status == OK ) { toLatLng = results[0].geometry.location; map.getRoutes({ origin: [ fromLatLng.lat(), fromLatLng.lng() ], destination: [ toLatLng.lat(), toLatLng.lng() ], travelMode: driving, unitSystem: imperial, callback: routeDone }); } }; var fromAddressDone = function( results, status ) { if ( status == OK ) { fromLatLng = results[0].geometry.location; GMaps.geocode({ address: toAddress, callback: toAddressDone }); } }; GMaps.geocode({ address: fromAddress, callback: fromAddressDone });   此外, async.js 库可以帮助我们处理多重Ajax requests/responses. 例如:  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 async.parallel([ function( done ) { GMaps.geocode({ address: toAddress, callback: function( result ) { done( null, result ); } }); }, function( done ) { GMaps.geocode({ address: fromAddress, callback: function( result ) { done( null, result ); } }); } ], function( errors, results ) { getRoute( results[0], results[1] ); });   这段代码执行两个异步函数,每个函数都接收一个名为done的回调函数并在函数结束的时候调用它。

当两个done回调函数结束后,parallel函数的回调函数被调用并执行或处理这两个异步函数产生的结果或错误。

  Promises模型  引自 Common *** /A:  promise表示一个操作独立完成后返回的最终结果。

  有很多库都包含了promise模型,其中jQuery已经有了一个可使用且很出色的promise API。

jQuery在1.5版本引入了Deferred对象,并可以在返回promise的函数中使用jQuery.Deferred的构造结果。

而返回promise的函数则用于执行某种异步操作并解决完成后的延迟。

  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var geocode = function( address ) { var dfd = new $.Deferred(); GMaps.geocode({ address: address, callback: function( response, status ) { return dfd.resolve( response ); } }); return dfd.promise(); }; var getRoute = function( fromLatLng, toLatLng ) { var dfd = new $.Deferred(); map.getRoutes({ origin: [ fromLatLng.lat(), fromLatLng.lng() ], destination: [ toLatLng.lat(), toLatLng.lng() ], travelMode: driving, unitSystem: imperial, callback: function( e ) { return dfd.resolve( e ); } }); return dfd.promise(); }; var doSomethingCoolWithDirections = function( route ) { // do something with route }; $.when( geocode( fromAddress ), geocode( toAddress ) ). then(function( fromLatLng, toLatLng ) { getRoute( fromLatLng, toLatLng ).then( doSomethingCoolWithDirections ); });   这允许你执行两个异步函数后,等待它们的结果,之后再用先前两个调用的结果来执行另外一个函数。

  promise表示一个操作独立完成后返回的最终结果。

  在这段代码里,geocode *** 执行了两次并返回了一个promise。

异步函数之后执行,并在其回调里调用了resolve。

然后,一旦两次调用resolve完成,then将会执行,其接收了之前两次调用geocode的返回结果。

结果之后被传入getRoute,此 *** 也返回一个promise。

最终,当getRoute的promise解决后,doSomethingCoolWithDirections回调就执行了。

  事件  事件是另一种当异步回调完成处理后的通讯方式。

一个对象可以成为发射器并派发事件,而另外的对象则监听这些事件。

这种类型的事件处理方式称之为 观察者模式 。

backbone.js 库在withBackbone.Events中就创建了这样的功能模块。

  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var SomeModel = Backbone.Model.extend({ url: /someurl }); var SomeView = Backbone.View.extend({ initialize: function() { this.model.on( reset, this.render, this ); this.model.fetch(); }, render: function( data ) { // do something with data } }); var view = new SomeView({ model: new SomeModel() });   还有其他用于发射事件的混合例子和函数库,例如 jQuery Event Emitter , EventEmitter , monologue.js ,以及node.js内建的 EventEmitter 模块。

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

  一个类似的派发消息的方式称为 中介者模式 , postal.js 库中用的即是这种方式。

在中介者模式,有一个用于所有对象监听和派发事件的中间人。

在这种模式下,一个对象不与另外的对象产生直接联系,从而使得对象间都互相分离。

  绝不要返回promise到一个公用的API。

这不仅关系到了API用户对promises的使用,也使得重构更加困难。

不过,内部用途的promises和外部接口的事件的结合,却可以让应用更低耦合且便于测试。

  在先前的例子里面,doSomethingCoolWithDirections回调函数在两个geocode函数完成后执行。

然后,doSomethingCoolWithDirections才会获得从getRoute接收到的响应,再将其作为消息发送出去。

  ? 1 2 3 4 5 var doSomethingCoolWithDirections = function( route ) { postal.channel( ui ).publish( directions.done, { route: route }); };   这允许了应用的其他部分不需要直接引用产生请求的对象,就可以响应异步回调。

而在取得命令时,很可能页面的好多区域都需要更新。

在一个典型的jQuery Ajax过程中,当接收到的命令变化时,要顺利的回调可能就得做相应的调整了。

这可能会使得代码难以维护,但通过使用消息,处理UI多个区域的更新就会简单得多了。

  ? 1 2 3 4 5 6 7 8 var UI = function() { this.channel = postal.channel( ui ); this.channel.subscribe( directions.done, this.updateDirections ).withContext( this ); }; UI.prototype.updateDirections = function( data ) { // The route is available on data.route, now just update the UI }; app.ui = new UI();   另外一些基于中介者模式传送消息的库有 amplify, PubSub *** , and radio.js。

  结论  JavaScript 使得编写异步代码很容易. 使用 promises, 事件, 或者命名函数来避免callback hell. 为获取更多javascript异步编程信息,请点击Async JavaScript: Build More Responsive Apps with Less . 更多的实例托管在github上,地址NetTutsAsync *** ,赶快Clone吧 !

相关文章

黑客教你查询某人信息,这个技术一定适合你_黑客查询

黑客教你查询某人信息,这个技术一定适合你因为质疑丈夫出轨,陈美莉行使闺蜜找来了一位“黑客”,对丈夫举行了把关。没想到,她掉进了一本《连环画》,被闺蜜以种种来由骗走了29万米。 黑客教你查询某人信息...

电脑版黑客模拟器(黑客模拟器下载安装)

电脑版黑客模拟器(黑客模拟器下载安装)

本文导读目录: 1、电脑安装PSP模拟器要怎么安装!需要下载那些东西! 2、小偷模拟器黑客电脑怎么用 3、求一款比较好的ONS模拟器 4、《灵魂黑客》汉化版,用PSP GO玩,战斗节奏很慢...

青岛找黑客,国际黑客网

一、青岛怎么找黑客 1、源码黑客网在DOS或Windows机器上学习黑客就像把铁绑在腿上学习跳舞一样。青岛国际有些人也是黑客,比如电子和音乐,你可以在任何更高水平的科学和艺术中找到它。弄回钱违法吗黑客...

狗狗基础动作训练方法(常见指令动作如何训练

狗狗基础动作训练方法(常见指令动作如何训练

狗狗的训练越早越好,最佳的时期是3个月到七个月,这是狗狗领悟能力最好的时候~训练的时候主人要有耐心哦,需要反复训练狗狗们才能记得住。还有口令要一致、一旦确定了口令就不能更改,否则狗狗会越来越糊涂不知道...

崩坏3什么武器好用 崩坏3新版本武器强度排行榜一览

崩坏3什么武器好用 崩坏3新版本武器强度排行榜一览

崩坏3新版本4.2上线,在这次的新版本里,除了角色以外,武器系统也有了很多的调整。因此在这次更新后,不仅是不同的人物有排行榜,武器同样也有了新的强度分级。那么大家知道具体的强度排名吗?这里就一起来看看...

由应急管理部管理的国陈丕显夫人家矿山安全监察局“三定

  由应急管理部管理的国家矿山安全监察局“三定方案”公布   由应急管理部管理的国家矿山安全监察局“三定方案”已于近日公布,该机构为副部级国家局,并在地方设立27个矿山安全监察局。   10月9日...