当前位置: 首页 > 后端技术 > Node.js

Javascript异步编程——延迟对象

时间:2023-04-03 11:21:36 Node.js

上一篇文章提到过,使用jquery的ajax方法操作ajax请求会遇到回调函数嵌套的问题。当然jQuery团队也发现了这个问题。2011年,jQuery1.5版本之后,jQuery.Deferred对象问世,解决了这类问题。后来,zapto等框架也推出了相同API的deferred对象来进行异步操作。jquery1.5版本后重写了ajax请求的内部实现。$.ajax方法返回的不再是一个jqXHR对象,而是一个Deferred对象。使用jquery1.5版本以后的代码,可以使用下面的方法来进行ajax请求。//前提是要引入jqueryvarfetchData=function(url){return$.ajax({type:'get',url:url});}这样,请求的内容就完成了,$.ajax返回一个$.Deferred对象。那么我们就可以使用$.Deferred对象的api来进行一些异步操作了。限于篇幅,这里只简单介绍一下.done、.fail、.then和$.when这三个方法。对于每个$.Deferred对象,实例都有多个方法。done方法表示异步完成时执行的方法,fail方法表示异步失败时执行的方法。这两个方法仍然同时返回$.Deferred对象的一个??实例。继续上面的ajax操作,我们可以这样写成功和失败的回调://fetchDatafollowedbyfetchData()//执行函数返回一个Deferred对象instance.done()//接受一个函数,ajax请求是successfulcalled.fail()//接受一个函数,ajax请求失败调用.done(),第二个表示失败时的执行方法。同样,它也返回一个延迟的对象实例。这意味着也可以进行链式调用。fetchData().then(successFn,??errorFn)//第一次回调.then(successFn,??errorFn)//第二次回调的内部实现,.done和.fail都是基于.then的fetchData()实现fetchData().done(successFn)<===>.then(successFn,??null).fail(errorFn)<===>.then(null,errorFn)对于多个ajax同时请求,执行同一个回调functiontogether上面,jquery有一个$.when方法,接受多个Deferred对象实例,同时执行。varfetchData=function(url){return$.ajax({type:'get',url:url});}varfetchData1=fetchData('/path/to/data1');varfetchData2=fetchData('/路径/to/data2');$.when(fetchData1,fetchData2,function(data1,data2){//fetchData1响应data1//fetchData2响应data2})完美解决了开发中的异步问题。上面的$.ajax只是在$.deferred对象上封装了一层ajax操作。实际上,真正的$.Deferred对象是这样调用的:functionprintA(){vardeferred=new$.Deferred();setTimeout(function(){console.log('A');deferred.resolve('完成。');},1000);returndeferred;}functionprintB(msg){vardeferred=new$.Deferred();setTimeout(function(){console.log('B'+msg);deferred.resolve();},1000);returndeferred;}printA().then(printA).then(printB)每个函数都维护一个Deferred对象,每个带有异步操作的函数执行成功后,指示全局deferred对象执行下一个函数,实现异步效果.$.Deferred实例deferred创建后,调用deferred.resolve()表示成功完成响应,调用deferred.reject()表示调用失败响应。源码的详细解释请参考另一篇文章。这里我们主要写这个调用方式实现的tiny版本。首先我们写一个Callback对象来维护我们的回调函数队列varCallbacks=function(){functionCallbacks(){this.callbacks=[];}Callbacks.prototype.add=function(fn){this.callbacks.add(fn);归还这个;}Callbacks.prototype.fire=function(){varlen=this.callbacks.length;if(len){this.callbacks.unshift()();}}returnCallbacks;}这段代码逻辑很简单,Callbacks对象有两个方法,分别是add和fire。调用add向当前回调数组添加一个函数。fire方法,从Callbacks中提取第一个回调并执行它。对于Deferred对象,我们至少需要两个方法,resolve和reject。进行成功和失败的调用。并且可以进行连锁调用。varDeferred=function(){this.successCbs=newCallbacks();this.errorCbs=newCallbacks();}Deferred.prototype.then=function(successCb,errorCb){this.successCbs.add(successCb);这。errorCbs.add(errorCb);返回这个;}Deferred.prototype.resolve=function(){this.successCbs.fire();返回这个;}Deferred.prototype.reject=function(){this.errorCbs.fire();returnthis;}在这个简单的完成之后,我们创建一个新的Deferred实例,然后我们就可以通过链式调用来进行异步操作了。vardeferred=newDeferred();functionprintA(){setTimeout(function(){console.log('A');deferred.resolve();},1000);returndeferred;}functionprintB(){setTimeout(function(){console.log('B');deferred.resolve();},1000);}printA().then(printB).then(printA)同理,我们可以封装一个自制的tiny-Deferred对象的tiny-ajax方法。varajax=function(options){varxhrOptions={类型:options.type||'get',url:options.url||'/默认/路径',异步:options.async||真的};vardeferred=newDeferred();varxhr=newXHRHttpRequest();xhr.open(xhrOptions.type,xhrOptions.url,xhrOptions.async);xhr.onload=function(result){deferred.resolve(result);}xhr.onerror=function()xhr.send();returndeferred;}具体源码已在Github上开源,欢迎pr和issuses。