源碼注釋
// Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. // // Some code (c) 2005, 2013 jQuery Foundation, Inc. and other contributors ;(function($){ var slice = Array.prototype.slice function Deferred(func) { //元組:描述狀態、狀態切換方法名、對應狀態執行方法名、回調列表的關系 //tuple引自C++/python,和list的區別是,它不可改變 ,用來存儲常量集 var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", $.Callbacks({once:1, memory:1}), "resolved" ], [ "reject", "fail", $.Callbacks({once:1, memory:1}), "rejected" ], [ "notify", "progress", $.Callbacks({memory:1}) ] ], state = "pending", //Promise初始狀態 //promise對象,promise和deferred的區別是: /*promise只包含執行階段的方法always(),then(),done(),fail(),progress()及輔助方法state()、promise()等。 deferred則在繼承promise的基礎上,增加切換狀態的方法,resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()*/ //所以稱promise是deferred的只讀副本 promise = { /** * 返回狀態 * @returns {string} */ state: function() { return state }, /** * 成功/失敗狀態的 回調調用 * @returns {*} */ always: function() { deferred.done(arguments).fail(arguments) return this }, /** * * @returns promise對象 */ then: function(/* fnDone [, fnFailed [, fnProgress]] */) { var fns = arguments //注意,這無論如何都會返回一個新的Deferred只讀副本, //所以正常為一個deferred添加成功,失敗,千萬不要用then,用done,fail return Deferred(function(defer){ $.each(tuples, function(i, tuple){ //i==0: done i==1: fail i==2 progress var fn = $.isFunction(fns[i]) && fns[i] //執行新deferred done/fail/progress deferred[tuple[1]](function(){ //直接執行新添加的回調 fnDone fnFailed fnProgress var returned = fn && fn.apply(this, arguments) //返回結果是promise對象 if (returned && $.isFunction(returned.promise)) { //轉向fnDone fnFailed fnProgress返回的promise對象 //注意,這里是兩個promise對象的數據交流 //新deferrred對象切換為對應的成功/失敗/通知狀態,傳遞的參數為 returned.promise() 給予的參數值 returned.promise() .done(defer.resolve) .fail(defer.reject) .progress(defer.notify) } else { var context = this === promise ? defer.promise() : this, values = fn ? [returned] : arguments defer[tuple[0] + "With"](context, values)//新deferrred對象切換為對應的成功/失敗/通知狀態 } }) }) fns = null }).promise() }, /** * 返回obj的promise對象 * @param obj * @returns {*} */ promise: function(obj) { return obj != null ? $.extend( obj, promise ) : promise } }, //內部封裝deferred對象 deferred = {} //給deferred添加切換狀態方法 $.each(tuples, function(i, tuple){ var list = tuple[2],//$.Callback stateString = tuple[3]// 狀態 如 resolved //擴展promise的done、fail、progress為Callback的add方法,使其成為回調列表 //簡單寫法: promise['done'] = jQuery.Callbacks( "once memory" ).add // promise['fail'] = jQuery.Callbacks( "once memory" ).add promise['progress'] = jQuery.Callbacks( "memory" ).add promise[tuple[1]] = list.add //切換的狀態是resolve成功/reject失敗 //添加首組方法做預處理,修改state的值,使成功或失敗互斥,鎖定progress回調列表, if (stateString) { list.add(function(){ state = stateString //i^1 ^異或運算符 0^1=1 1^1=0,成功或失敗回調互斥,調用一方,禁用另一方 }, tuples[i^1][2].disable, tuples[2][2].lock) } //添加切換狀態方法 resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith() deferred[tuple[0]] = function(){ deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments) return this } deferred[tuple[0] + "With"] = list.fireWith }) //deferred繼承promise的執行方法 promise.promise(deferred) //傳遞了參數func,執行 if (func) func.call(deferred, deferred) //返回deferred對象 return deferred } /** * * 主要用於多異步隊列處理。 多異步隊列都成功,執行成功方法,一個失敗,執行失敗方法 也可以傳非異步隊列對象 * @param sub * @returns {*} */ $.when = function(sub) { var resolveValues = slice.call(arguments), //隊列數組 ,未傳參數是[] len = resolveValues.length,//隊列個數 i = 0, remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0, //子def計數 deferred = remain === 1 ? sub : Deferred(),//主def,如果是1個fn,直接以它為主def,否則建立新的Def progressValues, progressContexts, resolveContexts, updateFn = function(i, ctx, val){ return function(value){ ctx[i] = this //this val[i] = arguments.length > 1 ? slice.call(arguments) : value // val 調用成功函數列表的參數 if (val === progressValues) { deferred.notifyWith(ctx, val) // 如果是通知,調用主函數的通知,通知可以調用多次 } else if (!(--remain)) { //如果是成功,則需等成功計數為0,即所有子def都成功執行了,remain變為0, deferred.resolveWith(ctx, val) //調用主函數的成功 } } } //長度大於1, if (len > 1) { progressValues = new Array(len) // progressContexts = new Array(len) resolveContexts = new Array(len) //遍歷每個對象 for ( ; i < len; ++i ) { //如果是def, if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) { resolveValues[i].promise() .done(updateFn(i, resolveContexts, resolveValues)) //每一個成功 .fail(deferred.reject)//直接掛入主def的失敗通知函數,當某個子def失敗時,調用主def的切換失敗狀態方法,執行主def的失敗函數列表 .progress(updateFn(i, progressContexts, progressValues)) } else { --remain //非def,直接標記成功,減1 } } } //都為非def,比如無參數,或者所有子隊列全為非def,直接通知成功,進入成功函數列表 if (!remain) deferred.resolveWith(resolveContexts, resolveValues) return deferred.promise() } $.Deferred = Deferred })(Zepto)
Promises/A+
由於deferred是基於Promise規范,我們首先需要理清楚Promises/A+是什么。
它的規范內容大致如下(此翻譯內容引自這里)
- 一個promise可能有三種狀態:等待(pending)、已完成(fulfilled)、已拒絕(rejected)
- 一個promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
- promise必須實現then方法(可以說,then就是promise的核心),而且then必須返回一個promise,同一個promise的then可以調用多次,並且回調的執行順序跟它們被定義時的順序一致
- then方法接受兩個參數,第一個參數是成功時的回調,在promise由“等待”態轉換到“完成”態時調用,另一個是失敗時的回調,在promise由“等待”態轉換到“拒絕”態時調用。同時,then可以接受另一個promise傳入,也接受一個“類then”的對象或方法,即thenable對象。
先用偽代碼來實現其規范內容
//普通的異步回調寫法。 function fA(){ var a1,a2; //出現異常,調用其他方法 try{ fa1(a1); fa2(a2); }catch(e){ efa1(a1); efa2(a2); } } function fa2(){ fB();//調用另一個和fA類似的異步回調 }
//下面采用Promise規范來改寫 //初始化: 等待狀態 pending var Promise = { status: pending, //狀態 promise: function(o){ return { done:done, fail:fail } }, //必須申明的then方法 then:function(fulfilledFn,rejectedFn){ this.done(fulfilledFn); this.fail(rejectedFn); //返回promise對象 return this; }, //當狀態切換fulfilled時執行 done: function(){ }, //當狀態切換rejected時執行 fail:function(){ }, //切換為已完成狀態 toFulfilled:function(){ this.status = 'fulfilled' }, //切換為已拒絕狀態 toRejected:function(){ this.status = 'rejected' } } //將函數包裝成Promise對象,並注冊完成、拒絕鏈方法 //通過then Promise.promise(fA).then(fa1,efa1).then(fa2,efa2); //假定fb里還調用了另一個異步FB, //之前fA的異步回調執行到fb方法 var PA = Promise.promise(fA).then(fa,efa).then(fb,efb); //再掛上fB的異步回調 PA.then(fB).then(fb1,efb1).then(fb2,efb2);
Promise規范生命周期
Deferred用法
Deferred生命周期
Deferred設計