目錄php
part1 deferred延遲對象jquery
part2 when輔助方法ajax
網盤源代碼 連接: https://pan.baidu.com/s/1skAj8Jj 密碼: izta數組
part1 deferred延遲對象promise
使用callbacks完成異步:瀏覽器
<script src="js/jquery-2.0.3.js"></script> <script> var cb=$.Callbacks(); setTimeout(function(){ alert(111); cb.fire(); },1000); cb.add(function(){ alert(222); }) //經過回調,先彈出111,再彈出222. </script>
使用deferred完成異步:異步
<script src="js/jquery-2.0.3.js"></script> <script> var dfd=$.Deferred(); setTimeout(function(){ alert(111); dfd.resolve(); }) dfd.done(function(){ alert(222); }) //經過延遲對象,先彈出111,再彈出222. </script>
咱們看到,二者代碼很是類似,其實deferred延遲對象本就是基於callbacks來開發的。deferred的resolve對應callbacks的fire,deferred的done對應callbacks的add。函數
延遲對象除了resolve狀態,還有不少其餘一些狀態。工具
//3046行,這裏定義了3套狀態 //resolve表明成功,reject表明失敗,notify表明通知與過程 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") ] ],
相似:測試
<script src="js/jquery-2.0.3.js"></script> <script> var dfd=$.Deferred(); setTimeout(function(){ alert(111); dfd.reject(); }) dfd.fail(function(){ alert(222); }) //延遲失敗,調用fail方法,那麼最終的結果是先彈出111,再彈出222. </script>
ajax內置了deferred對象哦。
<script src="js/jquery-2.0.3.js"></script> <script> $.ajax("xxx.php").done(function(){ alert("成功!") }).fail(function(){ alert("失敗"); }) </script>
咱們知道deferred的成功狀態包含resolve和done方法,失敗狀態包含reject和fail方法,進度中包含notify和progress方法。那麼在源碼中是如何映射起來的呢。
1.狀態映射
//把相同狀態的方法名都放到同一個數組裏 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") ] ],
//遍歷數組,獲得的元素也是一個數組,賦值給list jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; //狀態字符串,即resolved和rejected。 // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; //把done、fail添加到add方法裏 // 處理狀態字符串 if ( stateString ) { // list.add(function() { // 狀態字符串,即resolved和rejected state = stateString; // 一旦發生某個狀態,那麼對應的fire就會被禁用,或者progress就會上鎖。 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ]使用了callbacks的fire觸發 deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; });
2.once和memory功能
先來看callbacks。
<body> <button id="btn">按鈕</button> <script src="js/jquery-2.0.3.js"></script> <script> var cb=$.Callbacks("memory"); cb.add(function(){ alert("aaa"); }); cb.fire(); $("#btn").click(function(){ alert("bbb"); }) //當咱們點擊按鈕時,由於memory的記憶功能,點擊按鈕依然能彈出bbb。 </script> </body>
那麼相似的,deferred也有memory功能,參照3048行的代碼, jQuery.Callbacks("once memory") 。咱們使用progress/notify一組來測試。
<script src="js/jquery-2.0.3.js"></script> <script> var dfd=$.Deferred(); setInterval(function(){ alert(111); dfd.notify(); },1000); dfd.done(function(){ alert("成功"); }).fail(function(){ alert("失敗") }).progress(function(){ alert("進度中") }); //每隔一秒就會執行alert 111的任務,同時因爲deferred的記憶功能,因此也會反覆彈出"進度中"。 </script>
除了memory,那麼once的意思是隻有一次。
<script src="js/jquery-2.0.3.js"></script> <script> var dfd=$.Deferred(); setInterval(function(){ alert(111); dfd.resolve(); },1000); dfd.done(function(){ alert("成功"); }).fail(function(){ alert("失敗") }).progress(function(){ alert("進度中") }); //不管resolve每隔一秒執行多少次,最終的done方法也只執行一次。 </script>
1.promise對象與deferred對象的關係
在3095行開始的 jQuery.each( tuples, function( i, tuple ) { each方法裏,會把 promise[ done | fail | progress ] = list.add done、fail、progress方法添加到callbacks的add方法裏,這裏使用了promise對象。3113行的 deferred[ tuple[0] ] = function() { 則使用了deferred對象。那麼二者之間的關係是什麼呢?
promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } //... promise.promise( deferred );
咱們從這些源碼裏能夠看到,promise會繼承給deferred對象。
promise = { state: function() { return state; }, //.......
promise定義的方法包括state、always、then、promise、pipe、done、fail、progress等等,deferred定義的包括resolve、reject、notify方法,那麼經過繼承,deferred也就能夠擁有全部的方法了。那麼區別就是多出來的resolve、reject、notify方法。
這裏有一個應用:
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); setTimeout(function(){ dfd.resolve(); },1000); return dfd; } var dfd=aaa(); dfd.done(function(){ alert("成功"); }).fail(function(){ alert("失敗"); }); dfd.reject(); //因爲reject的修改,那麼彈出失敗 </script>
但是若是咱們返回的是promise對象,那麼是沒有暴露resolve或者reject等方法的,因此也就不存在能夠修改狀態了。
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); setTimeout(function(){ dfd.resolve(); },1000); return dfd.promise(); } var dfd=aaa(); dfd.done(function(){ alert("成功"); }).fail(function(){ alert("失敗"); }); dfd.reject();//報錯 //彈出成功字符串 </script>
state = "pending", //一開始state就是pending「進行中」 promise = { state: function() { return state; }, jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // Handle state if ( stateString ) { 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 ); }
1.首先,來演示state的各個狀態。
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); alert(dfd.state()); //首先彈出deferred對象的狀態 setTimeout(function(){ dfd.resolve(); alert(dfd.state()); //隔一秒以後已經成功了,那麼彈出狀態resolved },1000); return dfd.promise(); } var dfd=aaa(); dfd.done(function(){ alert("成功"); }).fail(function(){ alert("失敗"); }); </script>
結合deferred對象後面的代碼,整個執行結果是先彈出pending,而後彈出字符串「成功」,最後彈出resolved。爲何使用的是state方法,其實源代碼裏揭示的十分清楚,它是一個function。
state: function() {//3054行
2.接下來看源代碼是如何控制狀態的改變的。
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
咱們看到,只有resolve和reject纔有更新的狀態哦,因此對於notify狀態來講是沒有stateString的,因此也就不會走if語句。
jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; //第4個元素正好是狀態字符串 promise[ tuple[1] ] = list.add; // 處理狀態字符串 if ( stateString ) { //... }
3.最後看重要的if語句是如何控制的。
if ( stateString ) { list.add(function() { // 將[ resolved | rejected ]賦值給state state = stateString; }, tuples[ i ^ 1 ][ 2 ].disable, //由於tuple第3個元素就是回調jQuery.callbacks,那麼禁用掉 tuples[ 2 ][ 2 ].lock ); //第3個元素也就是關於notify的, //那麼再數第3個元素也是回調jQuery.callbacks,鎖住,就不會觸發了 }
能夠看到經過disable來禁用resolve和reject的回調,以及鎖住notify的狀態,那麼狀態一旦發生就不會改變了。
4.補充知識:
位運算符 tuples[ i ^ 1 ][ 2 ] 。
jQuery.each( tuples, function( i, tuple ) { //3095行,說明i取值爲0和1
<script> alert(1^1);//彈出0 alert(0^1);//彈出1 </script>
那麼也就是說若是i爲0,那麼取值爲1,若是i爲1,那麼取值爲0。因此對於禁用的原則是遍歷到誰,那麼其餘的禁用(有點取反的意思在哦)。
1.always方法
always: function() { deferred.done( arguments ).fail( arguments ); return this; }, //源碼對deferred對象,done方法、fail方法都調用傳入的回調
那麼看一個例子,就很是清晰了。
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); setTimeout(function(){ dfd.reject(); },1000); return dfd.promise(); } var dfd=aaa(); dfd.always(function(){ alert("不管狀態如何,調用這個回調函數"); }); </script>
2.then方法
3.promise方法
part2 when方法
when方法在源碼裏是一個輔助方法。它的使用和deferred延遲對象是有區別的,deferred只能針對一個延遲對象進行操做,when能針對多個延遲對象進行判斷。
1.when方法的延遲對象同時成功時觸發done方法。
DEMO演示:
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); dfd.resolve(); return dfd; } function bbb(){ var dfd=$.Deferred(); dfd.resolve(); return dfd; } $.when(aaa(),bbb()).done(function(){ alert("成功!"); }) </script>
瀏覽器顯示結果:
2.when方法只要有一個延遲對象的狀態是失敗,那麼就觸發fail函數。
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); dfd.resolve(); return dfd; } function bbb(){ var dfd=$.Deferred(); dfd.reject(); return dfd; } $.when(aaa(),bbb()).done(function(){ alert("成功!"); }).fail(function(){ alert("失敗"); }) </script>
瀏覽器顯示結果:
3.參數處理
無參、基本類型(好比123)、普通的函數並無返回promise,那麼就直接跳過參數。
<script src="js/jquery-2.0.3.js"></script> <script> $.when(123).done(function(){ alert("成功!"); }).fail(function(){ alert("失敗"); }) //基本數據類型123就直接跳過啦,那麼彈出成功字符串 </script>
那麼既然必定要一個知足返回promise的函數,爲何要這樣作呢,好處是可以傳參。
<script src="js/jquery-2.0.3.js"></script> <script> $.when(123).done(function(){ alert(arguments[0]); alert("成功!"); }).fail(function(){ alert("失敗"); }); //彈出123字符串和成功字符串。 </script>
咱們先來假設一個例子,它有4個延遲對象,那麼返回的是 return deferred.promise();//3181行 ,因此promise可使用done方法。那麼具體怎麼作的呢。
$.when(aaa(),bbb(),ccc(),ddd()).done(function(){ alert("成功!"); });
先看一個圖示。
DEMO演示:
<script src="js/jquery-2.0.3.js"></script> <script> $.when().done(function(){ alert("成功!"); }).fail(function(){ alert("失敗"); }) //由於無參,那麼跳過,最終彈出成功字符串。 </script>
進入源碼,3134行,
var i = 0, resolveValues = core_slice.call( arguments ), //這裏處理的是,往when方法傳基本數據的方式 length = resolveValues.length, //length就是傳入的參數個數,那麼無參就是0咯 remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, //因爲length取0,那麼說參數沒有那麼就取length也就是0.因此remaining計數器爲0
計數器remaining爲0,length爲0.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),es //也就不用執行咯 if ( length > 1 ) { } //if語句進不去
if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } //remaining爲0,進入if語句,因此deferred就被resolve了 return deferred.promise();
既然deferred被resolve了,那麼關於 $.when().done(function(){ 就能夠被執行了。
總之,若是無參,按照resolve來考慮。
DEMO演示:
<script src="js/jquery-2.0.3.js"></script> <script> $.when(123).done(function(){ alert("成功!"); }).fail(function(){ alert("失敗"); }) //基本數據類型123就直接跳過啦,那麼彈出成功字符串 </script>
進入源碼,那麼length爲1,
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, //有參數,並且判斷非promise函數,因此remaining計數器取0
接下來的updateFunc、length>1的if語句都不會走。!remaining的if語句會進去resolve。
if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise();
DEMO展現:
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); dfd.resolve(); //即便定義了延遲對象,但是並無返回promise對象 } $.when(aaa()).done(function(){ alert("成功!"); }).fail(function(){ alert("失敗"); }) //普通函數就直接跳過 </script>
進入源碼,那麼length爲1,
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, //有參數,並且判斷非promise函數,因此remaining計數器取0
接下來的updateFunc、length>1的if語句都不會走。!remaining的if語句會進去resolve。
if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise();
DEMO展現:
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); dfd.resolve(); return dfd; } $.when(aaa()).done(function(){ alert("成功!"); }).fail(function(){ alert("失敗"); }) //彈出成功字符串! </script>
進入源碼,length爲1,remaining計數器爲1.
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, //有參數,並且jQuery可以判斷爲返回promise的function,那麼最終計數器爲1
//這是主要的deferred deferred = remaining === 1 ? subordinate : jQuery.Deferred(), //計數器既然爲1,那麼deferred爲參數自己咯。
那麼接下來的length>1的if語句就不會進入了。!remaining的if語句也不會進入。這個時候返回
return deferred.promise(); //天然是參數所表明的promise
DEMO展現:
<script src="js/jquery-2.0.3.js"></script> <script> function aaa(){ var dfd=$.Deferred(); dfd.resolve(); return dfd; } function bbb(){ var dfd=$.Deferred(); dfd.resolve(); return dfd; } function ccc(){ var dfd=$.Deferred(); dfd.resolve(); return dfd; } $.when(aaa(),bbb(),ccc()).done(function(){ alert("成功!"); }).fail(function(){ alert("失敗"); }) //彈出成功字符串! </script>
針對Demo所示的參數,length爲3,remaining算下來就是length長度3.
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, //既然length不等於1,邏輯運算的結果爲1,那麼||運算符就不用看了 //因此結果取length
deferred = remaining === 1 ? subordinate : jQuery.Deferred(), //結果是jQuery.Deferred
if語句就要進入了。
// add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); //length爲多少,建立的數組就爲多少 //根據length遍歷 for ( ; i < length; i++ ) { if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { //遍歷若是正確的參數,那麼進入if語句 //與此同時else是把既有正常的參數,也有基本類型。 //那麼就把非promise的狀況計數器-1 resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); //對每一個參數進行處理,能夠看到done方法是全部的func都要成立 //而一旦某個fail了,就會致使master主deferred失敗 //progress是全部的func的狀態變化 } else { --remaining; } } }
經過遍歷,對每一個延遲對象進行處理。