var _Deferred=function(){ var callbacks=[],fired; var method={ done:function(func){ callbacks.push(func); if(fired){ method.resolveWith(fired[0],fired[1]); } return this }, resolveWith:function(context,args){ args=args || []; fired=[context,args]; while(callbacks[0]){ callbacks.shift().apply(context,args); } return this; }, resolve:function(){ method.resolveWith(this,arguments); return this; } } return method; } var Deferred=function(){ var doneDeferred=_Deferred(),failDeferred=_Deferred(); $.extend(doneDeferred,{ reject:failDeferred.resolve, rejectWidth:failDeferred.resolveWith, fail:failDeferred.done, always:function(func){ doneDeferred.done(func).fail(func); }, promise:function(){ return { done:doneDeferred.done, fail:doneDeferred.fail, always:doneDeferred.always, promise:doneDeferred.promise }; } }); return doneDeferred; } var when=function(deferredItem){ var args=arguments,length=arguments.length,count=length; var deferred=(length<=1&&deferredItem&&deferredItem.promise)?deferredItem:Deferred(); if(deferred!=deferredItem){ var doneBack=function(i){ return function(value){ args[i]=arguments.length>1?[].slice.call(arguments,0):value; if(!(--count)){ deferred.resolveWith(deferred,args); } } } for(var i=0;i<arguments.length;i++){ if(arguments[i] && arguments[i].promise){ arguments[i].done(doneBack(i)).fail(deferred.reject); }else{ count--; } } if(!count){ deferred.resolveWith(deferred,args); } } return deferred.promise(); } //测试 function wait(){ var df=Deferred(); setTimeout(function(){ alert('end wait..'); df.reject(); },2000); return df.promise(); } function wait2(){ var df=Deferred(); setTimeout(function(){ df.resolve(); },4000); return df.promise(); } function f1(){ alert('ttt'); } when(wait(),wait2()).done(function(){ f1(); });
延迟对象jQuery.Deferred()设计思想:
1)函数队列,done与fail各维护一个!(抽离出来封装一个闭包)
3)done/fail的时候把新的函数放进对应的函数队列
3)resolve的时候循环shift并调用done函数队列;reject的时候循环shift并调用fail函数队列
4)利用标志变量fired存储resolve或reject的上下文和参数,可用于判断deferred是否已resolve或reject,以及传递上下文和参数!(这样一来,done/fail方法在resolve/reject后面执行也不怕了)
5)when方法:判断是否所有deferred已resolve的时候,很巧妙地利用了count记数法,resolve一个count减去1(此方法可用于许多其他场景)