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