摘要:我們稱為回調(diào)對象,它內(nèi)部會維護(hù)一個數(shù)組,我們可以向其中添加若干個回調(diào)函數(shù),然后在某一條件下觸發(fā)執(zhí)行。第一次之后,再次新的回調(diào)函數(shù)時,自動執(zhí)行回調(diào)。當(dāng)前面的回調(diào)函數(shù)返回時,終止后面的回調(diào)繼續(xù)執(zhí)行。
最近懶癌發(fā)作,說好的系列文章,寫了一半,一直懶得寫,今天補(bǔ)上一篇。
Deferred我們在使用promise對象時,總會提到一個與它關(guān)系密切的對象——Deferred。其實(shí)Deferred沒什么內(nèi)容可講的,其實(shí)很簡單。
它包含一個promise對象
它可以改變對應(yīng)的promise的狀態(tài)
簡單的實(shí)現(xiàn)如下:
class Deferred{ constructor(){ let defer = {}; defer.promise = new Promise((resolve, reject)=>{ defer.resolve = resolve; defer.reject = reject; }) return defer; } }
我們知道promise對象內(nèi)部的狀態(tài),本身是在創(chuàng)建對象時傳入的函數(shù)內(nèi)控制,外部是訪問不到的,Deferred對象在它的基礎(chǔ)上包裝了一層,并提供了兩個在外部改變它狀態(tài)的方法。
用法其實(shí)在Promise介紹--規(guī)范篇中的例子內(nèi),多處使用到了,這里就不再贅述??傆X得文章寫這么點(diǎn)兒,就顯得太水了。。所以借此,講講jQuery中的實(shí)現(xiàn)。
jQuery中還有一個靜態(tài)方法$.Callbacks(),由于$.Deferred()強(qiáng)依賴它,所以我們先從它開刀。
$.Callbacks()$.Callbacks()我們稱為回調(diào)對象,它內(nèi)部會維護(hù)一個數(shù)組,我們可以向其中添加若干個回調(diào)函數(shù),然后在某一條件下觸發(fā)執(zhí)行。
有幾個方法從名字我們就知道它的作用是什么,add向數(shù)組內(nèi)部添加一個回調(diào)函數(shù),empty清空數(shù)組,fire觸發(fā)回調(diào)函數(shù),has數(shù)組中是否已經(jīng)添加某回調(diào)函數(shù),remove從數(shù)組中刪除某回調(diào)函數(shù)。
fireWith函數(shù)接收兩個參數(shù),第一個是回調(diào)函數(shù)執(zhí)行的上下文,第二個是回傳給回調(diào)函數(shù)的參數(shù)。fire中其實(shí)內(nèi)部調(diào)用的就是fireWith,其中第一個參數(shù)傳遞的是this。
其它的幾個函數(shù),都和回調(diào)數(shù)組的狀態(tài)有關(guān)。創(chuàng)建Callbacks對象時,接收一個字符串或者對象作為參數(shù)。其實(shí)內(nèi)部都會轉(zhuǎn)換為對象,這里不贅述,不同字符串表示不同的處理方式,一一介紹。
once :對象只會調(diào)用一次。
let cb = $.Callbacks("once") function a(){console.log("a")} function b(){console.log("b")} cb.add(a) cb.fire() cb.add(b) cb.fire() // a
第一次fire之后,回調(diào)列表之后不會再次觸發(fā)。
memory : 記住回調(diào)列表的執(zhí)行狀態(tài),如果回調(diào)函數(shù)fire過一次,之后每次add之后,則自動觸發(fā)該回調(diào)。
let cb = $.Callbacks("memory") function a(){console.log("a")} function b(){console.log("b")} cb.add(a) cb.fire() // a cb.add(b) // b
第一次fire之后,再次add新的回調(diào)函數(shù)b時,自動執(zhí)行回調(diào)b。
unique:每一個回調(diào)函數(shù)只可以添加一次。
let cb = $.Callbacks("unique") function a(){console.log("a")} function b(){console.log("b")} cb.add(a) cb.add(a) cb.fire() // a cb.add(b) cb.fire() // a // b
第一次fire時,只會打印一個a,說明第二個a沒有添加成功,但當(dāng)我們添加b時,是可以添加成功的。
stopOnFalse:當(dāng)前面的回調(diào)函數(shù)返回false時,終止后面的回調(diào)繼續(xù)執(zhí)行。
let cb = $.Callbacks("stopOnFalse") function a(){console.log("a");return false;} function b(){console.log("b")} cb.add(a) cb.add(b) cb.fire() // a
函數(shù)a返回了false,導(dǎo)致函數(shù)b沒有執(zhí)行。
我們再回過頭看$.Callbacks()對象的方法,lock方法表示鎖住回調(diào)數(shù)組,不再執(zhí)行,也就是模式為once時,調(diào)用一次fire后的狀態(tài),即在此之后不可以在此觸發(fā)。如下:
let cb = $.Callbacks() function a(){console.log("a")} function b(){console.log("b")} cb.add(a) cb.fire() cb.lock() cb.add(b) cb.fire()
lock之后,再次添加函數(shù)b并調(diào)用fire時,不會再次執(zhí)行,與once模式下效果類似。但如果是memory模式,回調(diào)先fire,然后再lock,之后再次add時,新添加的函數(shù)依然會執(zhí)行。
let cb = $.Callbacks("memory") function a(){console.log("a")} function b(){console.log("b")} cb.add(a) cb.fire() cb.lock() cb.add(b) // a // b
其實(shí)這種效果和直接創(chuàng)建回調(diào)對象時,參數(shù)設(shè)為once memory是一致的。也就是說,如下代碼與上面效果一致。
let cb = $.Callbacks("once memory") function a(){console.log("a")} function b(){console.log("b")} cb.add(a) cb.fire() cb.add(b) // a // b
我們發(fā)現(xiàn)這種once memory模式,正好與Promise中then方法添加的回調(diào)很類似。如果promise對象處于pending狀態(tài),則then方法添加的回調(diào)存儲在一個數(shù)組中,當(dāng)promise對象狀態(tài)改變(fire)時,執(zhí)行相應(yīng)的回調(diào),且之后再次通過then方法添加回調(diào)函數(shù),新回調(diào)會立刻執(zhí)行。同時,每一個回調(diào)只能執(zhí)行一次。所以,$.Deferred()內(nèi)部用的正好是once memory的Callbacks。
還有一個函數(shù)叫做disable,它的作用是直接禁用掉這個回調(diào)對象,清空回調(diào)數(shù)組,禁掉fire、add等。
locked用于判斷數(shù)組是否被鎖住,返回true或false。disabled用于判斷回調(diào)對象是否被警用,同樣返回true或false。
$.Deferred()有了以上的基礎(chǔ),我們接下來看看jQuery中,Deferred對象的實(shí)現(xiàn)。我們先看看它都有哪些方法。如圖:
別的方法我們暫且不關(guān)注,我們注意到里面有四個我們比較熟悉的方法,promise,reject,resolve。它們和我們前面說的Deferred對象中作用差不多。
jQuery的Deferred中有三個添加回調(diào)函數(shù)的方法done,fail,progress,分別對應(yīng)添加promise狀態(tài)為resolved、rejected和pending時的回調(diào),同時對應(yīng)有三個觸發(fā)回調(diào)函數(shù)的方法resolve、reject和notify。
我們接下來看看它內(nèi)部是怎么實(shí)現(xiàn)的。首先為每種狀態(tài)分別創(chuàng)建一個Callbacks對象,如下:
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")] ]
我們發(fā)現(xiàn)done、fail對應(yīng)的回調(diào)對象是once memory,而progress對應(yīng)的是memory。說明通過progress添加的函數(shù),可以多次重復(fù)調(diào)用。
然后定義了一個state用來保存狀態(tài),以及內(nèi)部的一個promise對象。
state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done(arguments).fail(arguments); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function(obj) { return obj != null ? jQuery.extend(obj, promise) : promise; } },
接下來會執(zhí)行一個循環(huán),如下:
// Add list-specific methods 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][4].disable, tuples[2][5].lock); } // deferred[ resolve | reject | notify ] deferred[tuple[0]] = function() { deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments); return this; }; deferred[tuple[0] + "With"] = list.fireWith; });
這一段代碼中我們可以看出,done、fail、progress其實(shí)就是add,resolve、reject和notify其實(shí)就是fire,與之對應(yīng)的resolveWith、rejectWith和notifyWith其實(shí)就是fireWith。且成功和失敗的回調(diào)數(shù)組中,會預(yù)先添加一個函數(shù),用來設(shè)置promise的狀態(tài)和禁用掉其它狀態(tài)下的回調(diào)對象。
從上面這段代碼中我們也可以看出,添加回調(diào)函數(shù)的方法,都是添加在promise對象上的,而觸發(fā)回調(diào)的方法是添加在deferred對象上的。代碼中會通過如下方法,把promise對象的方法合并到deferred對象上。
promise.promise(deferred);
所以,我們打印一下$.Deferred().promise()。
發(fā)現(xiàn)它確實(shí)比$.Deferred()少了那幾個觸發(fā)回調(diào)的方法。
其它的幾個方法我們簡單說一下,always會同時在成功和失敗的回調(diào)數(shù)組中添加方法。state是查看當(dāng)前promise對象的狀態(tài)。
then方法如下:
then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function(newDefer) { 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() .progress(newDefer.notify) .done(newDefer.resolve) .fail(newDefer.reject); } else { newDefer[tuple[0] + "With"]( this === promise ? newDefer.promise() : this, fn ? [returned] : arguments ); } }); }); fns = null; }).promise(); }
從代碼中我們可以看出,then方法和規(guī)范中的then方法類似,不過這里多了第三個參數(shù),是用于給progress添加回調(diào)函數(shù),同時返回一個新的promise對象。
pipe是為了向前兼容,它與then是相等的。
$.when()jQuery中還有一個與之相關(guān)的方法$.when(),它的作用類似于Promise.all()。具體實(shí)現(xiàn)方式基本是新建了一個Deferred對象,然后遍歷所有傳遞進(jìn)去的promise對象。不過添加了progres
的處理??傊鸵?guī)范有很多的不同,大家有興趣的就自己看一下吧。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/86653.html
摘要:比較下和也就是說返回值是的一個非狀態(tài)操作的子集,允許我們添加回調(diào),但是不允許我們操作的狀態(tài)。前面說了的返回值是一個新的對象,如果在新的對象上繼續(xù)添加回調(diào)會怎么樣呢我們分兩種情況來看。方法的返回值不是對象的返回值會傳遞給的參數(shù)。 前言 Deferred是從1.5版本引入的一個核心特性之一,主要是為了解決Callback Hell,老生常談的問題,這里就不多贅述了。本文旨在剖析Deferr...
摘要:簡要說明前面我寫了一篇方法封裝及文件設(shè)計(jì)文檔,主要用來說明我們在項(xiàng)目中通常會對的方法進(jìn)行進(jìn)一步的封裝處理,便于我們在業(yè)務(wù)代碼中使用。這篇文檔我們主要對封裝的方法進(jìn)行一個簡要說明。 簡要說明 前面我寫了一篇《jquery ajax 方法封裝及 api 文件設(shè)計(jì)》文檔,主要用來說明我們在項(xiàng)目中通常會對 jquery 的 ajax 方法進(jìn)行進(jìn)一步的封裝處理,便于我們在業(yè)務(wù)代碼中使用。從那篇文...
摘要:回調(diào)隊(duì)列對象,用于構(gòu)建易于操作的回調(diào)函數(shù)集合,在操作完成后進(jìn)行執(zhí)行。對象對象,用于管理回調(diào)函數(shù)的多用途列表。如果傳入一個延遲對象,則返回該對象的對象,可以繼續(xù)綁定其余回調(diào),在執(zhí)行結(jié)束狀態(tài)之后也同時調(diào)用其回調(diào)函數(shù)。 在工作中我們可能會把jQuery選擇做自己項(xiàng)目的基礎(chǔ)庫,因?yàn)槠涮峁┝撕啽愕腄OM選擇器以及封裝了很多實(shí)用的方法,比如$.ajax(),它使得我們不用操作xhr和xdr對象,直...
摘要:給普通的操作指定回調(diào)函數(shù)對象的最大優(yōu)點(diǎn),就是它把這一套回調(diào)函數(shù)接口,從操作擴(kuò)展到了所有操作。方法用于指定對象狀態(tài)為已失敗時的回調(diào)函數(shù)。執(zhí)行完畢執(zhí)行成功執(zhí)行失敗接收一個或多個對象作為參數(shù),為其指定回調(diào)函數(shù)。 什么是deferred對象 開發(fā)網(wǎng)站的過程中,我們經(jīng)常遇到某些耗時很長的javascript操作。其中,既有異步的操作(比如ajax讀取服務(wù)器數(shù)據(jù)),也有同步的操作(比如遍歷一個大型...
摘要:為這些回調(diào)函數(shù)分別命名并分離存放可以在形式上減少嵌套,使代碼清晰,但仍然不能解決問題。如果在一個結(jié)束成功或失敗,同前面的說明后,添加針對成功或失敗的回調(diào),則回調(diào)函數(shù)會立即執(zhí)行。 異步? 我在很多地方都看到過異步(Asynchronous)這個詞,但在我還不是很理解這個概念的時候,卻發(fā)現(xiàn)自己常常會被當(dāng)做已經(jīng)很清楚(* ̄? ̄)。 如果你也有類似的情況,沒關(guān)系,搜索一下這個詞,就可以得到大致...
閱讀 3053·2021-09-23 11:32
閱讀 3007·2021-09-22 15:12
閱讀 1774·2019-08-30 14:07
閱讀 3537·2019-08-29 16:59
閱讀 1721·2019-08-29 11:11
閱讀 2378·2019-08-26 13:50
閱讀 2481·2019-08-26 13:49
閱讀 2674·2019-08-26 11:49