deferred物件就是jQuery的回呼函式解決方案,它解決了如何處理耗時操作的問題,比如一些Ajax操作,影片操作等,(P.s:緊跟上一節:https://www.cnblogs.com/greatdesert/p/11433365.html的內容)
異步佇列有三種狀態:待定(pending)、成功(resolved)和失敗(rejected),初始時處于pending狀態
我們可以使用jQuery.Deferred創建一個異步佇列,回傳一個物件,該物件含有如下操作:
- done(fn/arr) ;添加成功回呼函式,當異步佇列處于成功狀態時被呼叫,引數同:jQuery.Callbacks(flags).add(fn/arr)
- fail(fn/arr) ;添加失敗回呼函式,引數同上
- progress(fn/arr) ;添加訊息回呼函式,引數同上
- then(donefn/arr,failfn/arr,profn/arr) ;同時添加成功回呼函式、失敗回呼函式和訊息回呼函式
- always(fn/arr) ;添加回呼函式到doneList和failList中,即保存兩份參考,當異步佇列處于成功或失敗狀態時被呼叫 ;引數可以是函式、函式串列
- state() ;回傳異步佇列的當前狀態、回傳pending、resolved或者rejected
- promise(obj) ;如果設定了obj引數物件則為obj物件增加異步佇列的方法并回傳,如果未設定引數obj,則回傳當前Deferred物件的只讀副本
如果呼叫$.Diferred()創建一個異步佇列,回傳后的物件可以呼叫如下幾個方法:
writer by:大沙漠 QQ:22969969
- resolve(args) ;使用指定的引數呼叫所有的成功回呼函式,異步佇列進入成功狀態,之后就無法再呼叫
- reject(args) ;使用指定的引數呼叫所有的失敗回呼函式,異步佇列進入失敗狀態
- notify(args) ;使用指定的引數呼叫所有的訊息回呼函式
- resolveWith(context,args) ;使用指定的背景關系和引數呼叫所有的成功回呼函式,異步佇列進入成功狀態
- rejectWith(context,args) ;使用指定的背景關系和引數呼叫所有的失敗回呼函式,異步佇列進入失敗狀態
- notifyWith(context,args) ;使用指定的背景關系和引數呼叫所有的訊息回呼函式
是不是和上一節介紹的Callbacks的fire和fireWith很像,Defferred內部就是通過通過Callback()回呼函式串列來實作的,例如:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title> <script src="http://libs.baidu.com/jquery/1.7.1/jquery.js"></script></head><body><script> function f1(x){console.log('f1 '+x);} function f2(x){console.log('f2 '+x);} function f3(x){console.log('f3 '+x);} var t = $.Deferred(); //創建異步佇列 t.done(f1).fail(f2).progress(f3); //添加成功回呼函式、失敗回呼函式和訊息串列回呼函式,等同于t.then(f1,f2,f3); t.notify('test'); //觸發所有訊息函式串列,輸出:f3 test t.resolve('done'); //觸發所有成功回呼函式,輸出:done t.reject('fail'); //沒有輸出,觸發成功回呼函式后失敗回呼函式串列就會被禁用,反過來也如此 t.progress(f1); //輸出:f1 test,這是因為訊息回呼函式之前已經被呼叫過,保存了背景關系和引數了,如果之前沒有呼叫,這里就沒有輸出,</script></body></html>
輸出如下:

原始碼分析
異步佇列內部的實作原理就是通過jQuery.Callbacks定義三個回呼函式串列,分別存盤成功、失敗、訊息回呼函式,然后回傳一個物件,在該物件上設定done、fail和progress,分別對應這三個回呼函式串列的add方法,這樣就實作分別添加不同狀態的回呼函式了
$.Deferred是通過jQuery.extend函式直接掛載到jQuery里的,結構如下:
jQuery.extend({ Deferred: function( func ) { var doneList = jQuery.Callbacks( "once memory" ), //成功回呼函式串列 failList = jQuery.Callbacks( "once memory" ), //失敗回呼函式串列 progressList = jQuery.Callbacks( "memory" ), //訊息回呼函式串列 ;這三個回呼函式串列就是存盤我們添加的觸發函式的 state = "pending", //初始狀態:pending lists = { //后面的方法會遍歷變數list中的屬性名來為異步佇列添加方法deffred.resolve()、resolveWith()、reject()、rejectWith()、notify()和notifyWith() resolve: doneList, reject: failList, notify: progressList }, promise = { //異步佇列的只讀副本 /*略*/ }, deferred = promise.promise({}), //異步佇列 key; for ( key in lists ) { //添加觸發成功、失敗、訊息回呼函式的方法,執行后deferred物件就多了resolve()、resolveWith()、reject()、rejectWith()、notify()和notifyWith()方法, deferred[ key ] = lists[ key ].fire; deferred[ key + "With" ] = lists[ key ].fireWith; } // Handle state deferred.done( function() { //添加三個成功回呼函式,分別用于設定狀態為成功(resolved),禁用失敗串列函式串列和鎖定訊息回呼函式串列 state = "resolved"; }, failList.disable, progressList.lock ).fail( function() { //添加三個失敗回呼韓素,分別用于設定狀態為失敗(rejected),禁用成功串列函式串列和鎖定訊息回呼函式串列 state = "rejected"; }, doneList.disable, progressList.lock ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; //回傳異步佇列deffrred }, //...})
可以看到$.Deferred最后回傳的是deferred這個布局變數,而deferred是promise.promise({})的回傳值,我們來看看promise的實作方法:
promise = { //異步佇列的只讀副本 done: doneList.add, //添加成功回呼函式,參考對應的回呼函式串列的callbacks.add(fn/arr)方法,下同 fail: failList.add, //添加失敗回呼函式 progress: progressList.add, //添加訊息回呼函式 state: function() { //回傳異步佇列的當前狀態:pending、resolved、rejected之一 return state; }, // Deprecated isResolved: doneList.fired, isRejected: failList.fired, then: function( doneCallbacks, failCallbacks, progressCallbacks ) { //同時添加成功回呼函式、失敗回呼函式和訊息回呼函式 deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); return this; }, always: function() { //添加回呼函式到doneList和failList中,即保存兩份參考,當異步佇列處于成功或失敗狀態時被呼叫 deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); return this; }, pipe: function( fnDone, fnFail, fnProgress ) { //過濾當前異步佇列的狀態和引數,并回傳一個新的異步佇列的副本,一般用不到 return jQuery.Deferred(function( newDefer ) { jQuery.each( { done: [ fnDone, "resolve" ], fail: [ fnFail, "reject" ], progress: [ fnProgress, "notify" ] }, function( handler, data ) { var fn = data[ 0 ], action = data[ 1 ], returned; if ( jQuery.isFunction( fn ) ) { deferred[ handler ](function() { returned = fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); } else { newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); } }); } else { deferred[ handler ]( newDefer[ action ] ); } }); }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { //如果設定了obj引數物件則為obj物件增加異步佇列的方法并回傳,如果未設定引數obj,則回傳當前Deferred物件的只讀副本, if ( obj == null ) { //如果obj為空 obj = promise; //則回傳promise物件(是上一層作用域鏈的promise物件) } else { for ( var key in promise ) { //否則遍歷promise obj[ key ] = promise[ key ]; //將promise的所有方法、屬性保存到obj中 } } return obj; //最后回傳obj }},$.Deferred就是對Callbacks回呼函式串列的管理而已,產生了一個新的$.Defferred介面,內部添加了一個state表示異步佇列的狀態,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/36822.html
標籤:jQuery
上一篇:按鍵批量操作示例
