jquery 原始碼分析之Deferred
阿新 • • 發佈:2019-01-01
一、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;
}
}