jquery 1.5 之 巧妙的Deferred

jquery 1.5 正式版已经出来半个月了,网上有不少blog对其评头论足,都提到说新加入一个Deferred函数 但始终没有一篇给力的东东让人一看就明白到底Deferred是个什么东东.粗略看了一下源码,研究一下它如何使用,在此做个记录...... 首先Jquery里面多了一个$._Deferred和一个$.Deferred,由"_"符号可以看出来$._Deferred是不想被用户直接使用的内部处理函数, 而$.Deferred才是让用户调用的接口,但$.Deferred是以$._Deferred为基础的,所以我们必须先分析一下$._Deferred, 首先分析 $._Deferred 为了方便查看,我直接把 $._Deferred 源码拷贝过来了: 源码: [javascript] // Create a simple deferred (one callbacks list) _Deferred: function() { var // callbacks list callbacks = [], // stored [ context , args ] fired, // to avoid firing when already doing so firing, // flag to know if the deferred has been cancelled cancelled, // the deferred itself deferred = { // done( f1, f2, ...) done: function() { if ( !cancelled ) { var args = arguments, i, length, elem, type, _fired; if ( fired ) { _fired = fired; fired = 0; } for ( i = 0, length = args.length; i < length; i++ ) { elem = args[ i ]; type = jQuery.type( elem ); if ( type === "array" ) { deferred.done.apply( deferred, elem ); } else if ( type === "function" ) { callbacks.push( elem ); } } if ( _fired ) { deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); } } return this; }, // resolve with given context and args resolveWith: function( context, args ) { //console.info(context,context.promise); if ( !cancelled && !fired && !firing ) { firing = 1; try { while( callbacks[ 0 ] ) { callbacks.shift().apply( context, args ); } } finally { fired = [ context, args ]; firing = 0; } } return this; }, // resolve with this as context and given arguments resolve: function() { deferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this, arguments ); return this; }, // Has this deferred been resolved? isResolved: function() { return !!( firing || fired ); }, // Cancel cancel: function() { cancelled = 1; callbacks = []; return this; } }; return deferred; }, [/javascript] 从源码中我们可以看到,$._Deferred函数返回一个deferred对象,包括 done,resolveWith,resolve,isResolved,cancel这5个方法,这个对象其实就是传说中的闭包, 这几个方法的作用分别是 done(arguments) 把要依次处理的函数(arguments)[可以是函数列表,也可以是函数数组]加入到队列表(callbacks)中,,返回 deferred 对象 resolveWith(context, args) 在给定的上下文(context默认为deferred=this对象)中执行队列,清除队列 resolve 执行队列,返回 deferred 对象 isResolved 队列是否执行完成 cancel 取消队列,返回 deferred 对象,队列被取消后,此$._Deferred函数生命周期结束 其中,done,resolve,resolveWith,cancel ,返回 deferred 对象,供链式调用. 以下我们为了方便理解,违背原则的直接使用 $._Deferred, 看实例: [javascript] function a(){console.info('函数a','此时执行环境是:',this,'可用参数有:',arguments);}; function b(){console.info('函数b','此时执行环境是:',this,'可用参数有:',arguments);}; var _testDefer = $._Deferred(); //加入并执行队列,在默认环境(context默认为deferred=this对象)中执行 _testDefer.done(a,b).resolve({a:'dd'},[1,2,3,4]); //加入并执行队列,在环境(context此时为{a:'dd'})中执行 _testDefer.done(a,b).resolveWith({a:'dd'},[1,2,3,4]); //多次加入调用执行队列,此时要注意,由于done函数的机制决定,第二次加入的队列直接用resolveWith在第一次的执行环境执行中执行掉,所以后续执行环境和参数不能改变,沿用第一次的. _testDefer.done(a,b).resolve({a:'dd'},[1,2,3,4]).done([a,b]).done([a,b]); //执行两次后,断裂... _testDefer.done(a,b).resolve({a:'dd'},[1,2,3,4]).done([a,b]).cancel().done([a,b]); [/javascript] 理解了$._Deferred 我们再来看 $.Deferred 源码: [javascript] // Full fledged deferred (two callbacks list) Deferred: function( func ) { var deferred = jQuery._Deferred(), failDeferred = jQuery._Deferred(), promise; // Add errorDeferred methods, then and promise jQuery.extend( deferred, { then: function( doneCallbacks, failCallbacks ) { deferred.done( doneCallbacks ).fail( failCallbacks ); return this; }, fail: failDeferred.done, rejectWith: failDeferred.resolveWith, reject: failDeferred.resolve, isRejected: failDeferred.isResolved, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj , i ) {//internal if ( obj == null ) { if ( promise ) { return promise; } promise = obj = {}; } i = promiseMethods.length; while( i-- ) { obj[ promiseMethods[ i ] ] = deferred[ promiseMethods[ i ] ]; } return obj; } } ); // Make sure only one callback list will be used deferred.then( failDeferred.cancel, deferred.cancel ); // Unexpose cancel delete deferred.cancel; // Call given func if any if ( func ) { func.call( deferred, deferred ); } return deferred; }, [/javascript] $._Deferred 首先建立两个 _Deferred() 对象 var deferred = jQuery._Deferred(), failDeferred = jQuery._Deferred(), 然后对 deferred 进行扩展:扩展了then,fail,rejectWith,reject,isRejected,最后是一个promise方法 再来看源码: then: function( doneCallbacks, failCallbacks ) { deferred.done( doneCallbacks ).fail( failCallbacks ); return this; }, fail: failDeferred.done, rejectWith: failDeferred.resolveWith, reject: failDeferred.resolve, isRejected: failDeferred.isResolved, 这么一处理等于是把 failDeferred 附加(拷贝)到 deferred 对象上去了,如果单单是为了拷贝直接一个对象就行了,但这里最主要的是为了控制对象体中的私有变量 fired,通过这个私有变量来确定对象体中的队列是否要执行,以下是一个简单的例子,看懂了,就算全明白了. [javascript] function a(who){ var fired; return { done: function(list){ if(fired){ console.log(who,fired,"被执行过了...",list); } return this; }, resolve: function(){ fired = true; return this; } } } function c(){ var aa = a('this is a'), bb = a('this is b'); aa.fail = bb.done; aa.reject = bb.resolve; aa.then = function(){ aa.done(arguments[0]).fail(arguments[1]); } return aa; }; //a 的 resolve执行,改变aa对象中的私有变量 c().done([7,8,9]).resolve().then([1,2,3],[4,5,6]); //a 的 reject执行,改变bb对象中的私有变量 c().done([7,8,9]).reject().then([1,2,3],[4,5,6]); [/javascript] 最后还有一个promise方法,jquery的Deferred方法为了实现多个函数队列之间的链式调用,添加了这么一个promise方法 从源码中我们可以知道,其实这个方法只返回一个包含 then done fail isResolved isRejected promise 这 6 个方法的对象 其中then是done,fail方法的合成体,也就是说then可以直接把done和fail方法写到一起,具体请看源码. 这样,一个队列执行完之后,只要返回这么一个对象,就可以用这个对象的,then,done,fail方法再把其它队列添加进去,根据isResolved和isRejected执行状态来确定具体执行那个队列分支,以此类推,可以一直这么执行下去......
posted @ 2011-02-15 11:24  7hihi  阅读(168)  评论(0编辑  收藏  举报