1. 程式人生 > >jquery 原始碼分析之Deferred

jquery 原始碼分析之Deferred

一、Deferred物件

      Deferred物件就是jQuery的回撥函式解決方案。Deferred的物件的含義就是延遲到未來某一個點再執行。它解決了如何處理耗時操作的問題,對那些操作提供了更好的控制

     ,以及統一的程式設計介面。

二、用法舉例

var dfd= $.Deferred();

    dfd.done(function(){
        alert("Done");
    }).fail(function(){
        alert("failed")
    }).progress(function(){
        alert("progress");
    });
    dfd.notify();
    dfd.resolve();
    //dfd.reject();

三、改變了傳統$.ajax的寫法

傳統的ajax請求是

$.ajax({
   url:"./test.txt",
    success:function(data){
          alert(data);
       },
    error:function(){
           alert("error");
        }
});
   $.ajax()操作完成之後,如果是低於1.5.0的版本的jQuery,返回的是XHR物件,你沒法進行鏈式操作;如果高於1.5.0版本,返回的是Deferred的物件,可以進行鏈式操作。
$.ajax("./test.txt").done(function(data){
        alert(data);
    }).fail(function(){
        alert("failed")
    });

done相當於是success方法,fail相當於是error方法。

四、原始碼分析

 1.可以傳入引數

$.extend({
    Deferred:function(func){
if ( func ) {
    func.call( deferred, deferred );
   }

// All done!
  return deferred;
  }
})
Deferred物件是擴充套件到了jquery物件上,是一個工具方法。可以傳入引數。當傳入引數時,該引數如果是函式就會立即執行。呼叫Deferred方法,最後返回一個deferred物件。

2.Deferred物件的幾個方法和狀態

var tuples = [
	// action, add listener, listener list, final state
      [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
      [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
      [ "notify", "progress", jQuery.Callbacks("memory") ]
	],

Deferred有3種類型。事件新增有done 、fail和progress,這三個方法對應著Callbacks物件的add方法。事件觸發有resolve、reject和notify,這三種方法對應著Callbacks物件的fire方法。done和resolve代表事件成功,fail和reject對應著事件執行失敗,progress和notify對應著進行中。

 其中,resolve和reject只觸發一次,因為jQuery.Callbacks("once memory"),而notify可以觸發多次。事件成功和事件失敗,有對應的狀態resolved和rejected,事件進行中則沒有狀態。

那麼這是如何對應的呢?

// Add list-specific methods
	jQuery.each( tuples, function( i, tuple ) {
		var list = tuple[ 2 ],   //Callbacks物件
			stateString = tuple[ 3 ];  //狀態

			// promise[ done | fail | progress ] = list.add
			promise[ tuple[1] ] = list.add;     //給promise物件新增done fail progress方法。

			// Handle state
			if ( stateString ) {          //如果有狀態的話,進入if
				list.add(function() {
					// state = [ resolved | rejected ]
					state = stateString;    

				// [ reject_list | resolve_list ].disable; progress_list.lock
				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );     //i=0時,i^1=1,也就是說,如果執行了事件成功的done那麼,後面的事件失敗對應的Callbacks物件就會失效。同時,進度事件鎖住,不會被觸發。
			}

			// deferred[ resolve | reject | notify ]
			deferred[ tuple[0] ] = function() {
				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );  //觸發fireWith事件
				return this;
			};
			deferred[ tuple[0] + "With" ] = list.fireWith;
		});

3.promise物件

    3.1 用法舉例

  一般的Deferred物件有個問題,就是狀態會被改變。

 var dfd= $.Deferred(),
        d,//
        wait=function(dfd){
            var task=function(){
                alert("Done");
                dfd.resolve();
            }
            setTimeout(task,500);
            return dfd;
        };
     d=wait(dfd);
     $.when(d)
        .done(function(){
            alert("Finished")
        })
        .fail(function(){
            alert("Failed");
        });
d.resolve();

在外部,改變了dfd物件的狀態,那麼回撥的時候就不會再執行第二次的事件函式。

為了避免Deferred物件的狀態被外部改變,jquery提供了deferred.promise()函式,該函式是promise物件的一個方法。

promise.promise( deferred );
promise: function( obj ) {
	return obj != null ? jQuery.extend( obj, promise ) : promise;
	}

當函式裡面有引數時,會被promise物件屬性和方法都擴充套件到入參,如果沒有入參,直接返回promise物件本身。而promise物件本身沒有resolve和reject方法。不能改變Deferred物件的狀態。
var dfd= $.Deferred(),
        d,//
        wait=function(dfd){
            var task=function(){
                alert("Done");
                dfd.resolve();
            }
            setTimeout(task,500);
            return dfd.promise();
        };
 d=wait(dfd);
 $.when(d)
        .done(function(){
            alert("Finished")
        })
        .fail(function(){
            alert("Failed");
        });
 d.resolve();//報錯

    3.2 原始碼解析

                   promise = {
				state: function() {
					return state;
				},
				always: function() { 
					deferred.done( arguments ).fail( arguments );//無論成功還是失敗都會執行同一個函式
					return this;
				},
				then: function( /* fnDone, fnFail, fnProgress */ ) {//簡寫,裡面的3個入參分別代表成功、失敗和進行中要執行的函式
					var fns = arguments;
					return jQuery.Deferred(function( newDefer ) {<span style="white-space:pre">	</span>  //Deferred()裡面的函式會議立即執行,傳入的引數時deferred物件
						jQuery.each( tuples, function( i, tuple ) {
							var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
							// deferred[ done | fail | progress ] for forwarding actions to newDefer
							deferred[ tuple[1] ](function() {
								var returned = fn && fn.apply( this, arguments );  //執行回撥函式
								if ( returned && jQuery.isFunction( returned.promise ) ) {
									returned.promise()
										.done( newDefer.resolve )
										.fail( newDefer.reject )
										.progress( newDefer.notify );
								} else {//對應Callbacks物件的fireWith方法
				  newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
								}
							});
						});
						fns = null;
					}).promise();
				},
				// Get a promise for this deferred
				// If obj is provided, the promise aspect is added to the object
				promise: function( obj ) {  //有引數時,把promise物件擴充套件到入參物件上,沒有返回promise物件
					return obj != null ? jQuery.extend( obj, promise ) : promise;
				}
			}