欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在jQuery1.5中使用deferred對象 著放大鏡看Promise

 更新時間:2011年03月12日 17:23:32   作者:  
在那篇經(jīng)典的關(guān)于jQuery1.5中Deferred使用方法介紹的文章中(譯文見這里),有下面一段描述
引言

在那篇經(jīng)典的關(guān)于jQuery1.5中Deferred使用方法介紹的文章中(譯文見這里),有下面一段描述:

$.ajax() returns an object packed with other deferred-related methods. I discussed promise(), but you'll also find then(), success(), error(), and a host of others. You don't have access to the complete deferred object, though; only the promise, callback-binding methods, and the isRejected() and isResolved() methods, which can be used to check the state of the deferred.
But why not return the whole object? If this were the case, it would be possible to muck with the works, maybe pragmatically "resolve" the deferred, causing all bound callbacks to fire before the AJAX request had a chance to complete. Therefore, to avoid potentially breaking the whole paradigm, only return the dfd.promise().


這段話非常令人費解,我也是看了幾遍才看明白。大致的意思是:
$.ajax()返回一個對象(jqXHR,這是對原生的XMLHttpRequest的封裝),這個對象包含了deferred相關(guān)的函數(shù),比如promise(), then(), success(), error(), isRejected(), isResolved()。
但是你發(fā)現(xiàn)沒,這里面沒有resolve(), resolveWith(), reject(), rejectWith() 幾個函數(shù),而這幾個函數(shù)才是用來改變deferred對象流程。也就是說$.ajax()返回了一個只讀的deferred對象。

下面erichynds改用反問的語氣提出,為什么不返回完整的deferred對象,而只返回只讀的deferred對象?
如果返回完整的deferred對象,那么外部程序就能隨意的觸發(fā)deferred對象的回調(diào)函數(shù),很有可能在AJAX請求結(jié)束前就觸發(fā)了回調(diào)函數(shù)(resolve),這就是與AJAX本身的邏輯相違背了。
所以為了避免不經(jīng)意間改變?nèi)蝿?wù)的內(nèi)部流程,我們應(yīng)該只返回deferred的只讀版本(dfd.promise())。


為了說明$.ajax()和$.Deferred()返回的deferred對象的不同,請看下面的例子:
復(fù)制代碼 代碼如下:

// deferred對象所有的方法數(shù)組
var methods = 'done,resolveWith,resolve,isResolved,then,fail,rejectWith,reject,isRejected,promise'.split(','),
method,
ajaxMethods = [],
onlyInDeferredMethods = [];
for (method in $.ajax()) {
if ($.inArray(method, methods) !== -1) {
ajaxMethods.push(method);
}
}

for (method in $.Deferred()) {
if ($.inArray(method, methods) !== -1 && $.inArray(method, ajaxMethods) === -1) {
onlyInDeferredMethods.push(method);
}
}
// 存在于$.Deferred(),但是不存在于$.ajax()的deferred相關(guān)方法列表為:
// ["resolveWith", "resolve", "rejectWith", "reject"]
console.log(onlyInDeferredMethods);

反面教材
如果$.ajax()返回的對象包含resolve(), resolveWith(),可能會產(chǎn)生哪些影響呢?
我們還是用例子也說明,首先看看erichynds原文的第一個例子:
復(fù)制代碼 代碼如下:

// $.get, 異步的AJAX請求
var req = $.get('./sample.txt').success(function (response) {
console.log('AJAX success');
}).error(function () {
console.log('AJAX error');
});

// 添加另外一個AJAX回調(diào)函數(shù),此時AJAX或許已經(jīng)結(jié)束,或許還沒有結(jié)束
// 由于$.ajax內(nèi)置了deferred的支持,所以我們可以這樣寫
req.success(function (response) {
console.log('AJAX success2');
});

console.log('END');

執(zhí)行結(jié)果為:
END -> AJAX success -> AJAX success2

下面修改jQuery1.5源代碼,為$.ajax()的返回值添加resolve()和resolveWith()函數(shù):
復(fù)制代碼 代碼如下:

// Attach deferreds
deferred.promise( jqXHR );
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
jqXHR.complete = completeDeferred.done;
// 下面兩行是我們手工增加的,jQuery源代碼中沒有
jqXHR.resolve = deferred.resolve;
jqXHR.resolveWith = deferred.resolveWith;

然后,執(zhí)行下面代碼:
復(fù)制代碼 代碼如下:

// $.get, 異步的AJAX請求
var req = $.get('./sample.txt').success(function (response) {
console.log('AJAX success');
}).error(function () {
console.log('AJAX error');
});

req.resolve();

// 添加另外一個AJAX回調(diào)函數(shù),此時AJAX或許已經(jīng)結(jié)束,或許還沒有結(jié)束
// 由于$.ajax內(nèi)置了deferred的支持,所以我們可以這樣寫
req.success(function (response) {
console.log('AJAX success2');
});

console.log('END');

此時的執(zhí)行結(jié)果為:
AJAX success -> AJAX success2 -> END

也就是說,在真實的AJAX請求結(jié)束之前,success的回調(diào)函數(shù)就已經(jīng)被觸發(fā)了,出現(xiàn)錯誤。

為了更清楚的看清這一切,我們手工給success回調(diào)函數(shù)傳遞一些偽造的參數(shù):
復(fù)制代碼 代碼如下:

// $.get, 異步的AJAX請求
var req = $.get('./sample.txt').success(function (response) {
console.log('AJAX success(' + response + ')');
});

req.resolve('Fake data');

// 添加另外一個AJAX回調(diào)函數(shù),此時AJAX或許已經(jīng)結(jié)束,或許還沒有結(jié)束
// 由于$.ajax內(nèi)置了deferred的支持,所以我們可以這樣寫
req.success(function (response) {
console.log('AJAX success2(' + response + ')');
});

console.log('END');

此時的執(zhí)行結(jié)果為:
AJAX success(Fake data) -> AJAX success2(Fake data) -> END

代碼分析
在深入jQuery代碼之前,先來看看jQuery.promise的文檔:
The deferred.promise() method allows an asynchronous function to prevent other code from interfering with the progress or status of its internal request. The Promise exposes only the Deferred methods needed to attach additional handlers or determine the state (then, done, fail, isResolved, and isRejected), but not ones that change the state (resolve, reject, resolveWith, and rejectWith).
If you are creating a Deferred, keep a reference to the Deferred so that it can be resolved or rejected at some point. Return only the Promise object via deferred.promise() so other code can register callbacks or inspect the current state.

大致意思是說,deferred.promise()用來阻止其它代碼修改異步任務(wù)的內(nèi)部流程。Promise的對象只對外公開添加回調(diào)函數(shù)和檢測狀態(tài)的函數(shù),而不包含修改狀態(tài)的函數(shù)。
如果你手工創(chuàng)建了一個deferred對象,那么你要維持對這個deferred對象的引用,以此來修改狀態(tài)觸發(fā)回調(diào)函數(shù)。不過你的返回值應(yīng)該是deferred.promise(),這樣外部程序可以添加回調(diào)函數(shù)或檢測狀態(tài),而不能修改狀態(tài)。

至此,大家對promise應(yīng)該有清晰的認識了。我們再來看下面兩段代碼,它們完成的功能完全一致:
復(fù)制代碼 代碼如下:

function getData() {
return $.get('/foo/');
}

function showDiv() {
// 正確代碼。推薦做法。
return $.Deferred(function (dfd) {
$('#foo').fadeIn(1000, dfd.resolve);
}).promise();
}

$.when(getData(), showDiv()).then(function (ajaxResult) {
console.log('The animation AND the AJAX request are both done!');
});

復(fù)制代碼 代碼如下:

function getData() {
return $.get('/foo/');
}

function showDiv() {
// 正確代碼。不推薦這么做。
return $.Deferred(function (dfd) {
$('#foo').fadeIn(1000, dfd.resolve);
});
}

$.when(getData(), showDiv()).then(function (ajaxResult) {
console.log('The animation AND the AJAX request are both done!');
});

雖然上面兩段代碼完成相同的任務(wù),并且似乎第二段代碼更加簡潔,但是第二段代碼卻不是推薦的做法。
因為任務(wù)(showDiv)本身狀態(tài)的更改應(yīng)該保持在任務(wù)內(nèi)部,而不需要對外公開,對外只需要公開一個promise的只讀deferred對象就行了。

最后,我們來看看Deferred相關(guān)源代碼:
復(fù)制代碼 代碼如下:

// Promise相關(guān)方法數(shù)組
promiseMethods = "then done fail isResolved isRejected promise".split( " " ),

jQuery.extend(
// 完備的deferred對象(具有兩個回調(diào)隊列)
Deferred: function (func) {
var deferred = jQuery._Deferred(),
failDeferred = jQuery._Deferred(),
promise;
// 添加then, promise 以及出錯相關(guān)的deferred方法
jQuery.extend(deferred, {
then: function (doneCallbacks, failCallbacks) {
deferred.done(doneCallbacks).fail(failCallbacks);
return this;
},
fail: failDeferred.done,
rejectWith: failDeferred.resolveWith,
reject: failDeferred.resolve,
isRejected: failDeferred.isResolved,
// 返回deferred對象的只讀副本
// 如果將obj作為參數(shù)傳遞進去,則promise相關(guān)方法將會添加到這個obj上
promise: function (obj) {
if (obj == null) {
if (promise) {
return promise;
}
promise = obj = {};
}
var i = promiseMethods.length;
while (i--) {
obj[promiseMethods[i]] = deferred[promiseMethods[i]];
}
return obj;
}
});
// 確保只有一個回調(diào)函數(shù)隊列可用,也就是說一個任務(wù)要么成功,要么失敗
deferred.done(failDeferred.cancel).fail(deferred.cancel);
// 刪除cancel函數(shù)
delete deferred.cancel;
// 將當前創(chuàng)建的作為參數(shù)傳遞到給定的函數(shù)中
if (func) {
func.call(deferred, deferred);
}
return deferred;
});

如果你覺得上面的代碼閱讀比較困難,沒關(guān)系我寫了一個簡單的類似代碼:
復(fù)制代碼 代碼如下:

Arr = function () {
var items = [],
promise,
arr = {
add: function (item) {
items.push(item);
},
length: function () {
return items.length;
},
clear: function () {
items = [];
},
promise: function () {
if (promise) {
return promise;
}
var obj = promise = {};
obj.add = arr.add;
obj.length = arr.length;
obj.promise = arr.promise;
return obj;
}
};
return arr;
}

上面代碼定義了一個Arr,用來生成一個數(shù)組對象,包含一些方法,比如add(), length(), clear(), promise()。
其中promise()返回當前Arr對象的一個副本,只能向其中添加元素,而不能清空內(nèi)部數(shù)組。
復(fù)制代碼 代碼如下:

var arr = Arr();
arr.add(1);
arr.add(2);
// 2
console.log(arr.length());
arr.clear();
// 0
console.log(arr.length());
var arr = Arr();
arr.add(1);
arr.add(2);
// 2
console.log(arr.length());
var promise = arr.promise();
promise.add(3);
promise.add(4);
// 4
console.log(promise.length());
// Error: TypeError: promise.clear is not a function
promise.clear();

deferred.promise()與deferred.promise().promise()
還記得前面提到的那兩個完成相同功能的代碼么?
復(fù)制代碼 代碼如下:

function getData() {
return $.get('/foo/');
}

function showDiv() {
// 這里返回promise()或者直接返回deferred對象,代碼都能正確運行。
return $.Deferred(function (dfd) {
$('#foo').fadeIn(1000, dfd.resolve);
}).promise();
}

$.when(getData(), showDiv()).then(function (ajaxResult) {
console.log('The animation AND the AJAX request are both done!');
});

那么你有沒有思考過,為什么這兩種方式都能運行呢?
如果你深入jQuery的源代碼,你會發(fā)現(xiàn)$.when(obj1, obj2, ...)在內(nèi)部實現(xiàn)時會獲取obj1.promise():
復(fù)制代碼 代碼如下:

if ( object && jQuery.isFunction( object.promise ) ) {
object.promise().then( iCallback(lastIndex), deferred.reject );
}

所以我們來看上面showDiv的返回結(jié)果:
如果是deferred對象的話,$.when()通過下面方式得到promise:
$.Deferred().promise()

如果是deferred.promise()對象的話,$.when()通過下面方式得到promise:
$.Deferred().promise().promise()

那么是不是說:$.Deferred().promise() === $.Deferred().promise().promise()
我們還是通過示例來驗證我們的想法:
復(fù)制代碼 代碼如下:

var deferred = $.Deferred(),
promise = deferred.promise();
// true
promise === promise.promise();
// true
promise === promise.promise().promise().promise();

當然,這個結(jié)果是推理出來的,如果我們直接看Deferred的源代碼,也很容易看出這樣的結(jié)果:
復(fù)制代碼 代碼如下:

promise: function (obj) {
if (obj == null) {
// 在這里,如果promise已經(jīng)存在(已經(jīng)調(diào)用過.promise()),就不會重新創(chuàng)建了
if (promise) {
return promise;
}
promise = obj = {};
}
var i = promiseMethods.length;
while (i--) {
obj[promiseMethods[i]] = deferred[promiseMethods[i]];
}
return obj;
}

總結(jié)

1. deferred.promise()返回的是deferred對象的只讀屬性。
2. 建議任務(wù)不要返回deferred對象,而是返回deferred.promise()對象。這樣外部就不能隨意更改任務(wù)的內(nèi)部流程。
3. deferred.promise() === deferred.promise().promise() (上面我們分別從代碼推理,和源代碼分析兩個角度得到這個結(jié)論)

本文由三生石上原創(chuàng),博客園首發(fā),轉(zhuǎn)載請注明出處。

相關(guān)文章

  • jQuery.parseJSON(json)將JSON字符串轉(zhuǎn)換成js對象

    jQuery.parseJSON(json)將JSON字符串轉(zhuǎn)換成js對象

    本節(jié)主要介紹了使用jQuery.parseJSON(json)將JSON字符串轉(zhuǎn)換成js對象,需要的朋友可以參考下
    2014-07-07
  • jQuery多級彈出菜單插件ZoneMenu

    jQuery多級彈出菜單插件ZoneMenu

    這篇文章主要介紹了jQuery多級彈出菜單插件ZoneMenu的使用方法,有需要的小伙伴可以參考下
    2014-12-12
  • jQuery中的insertBefore(),insertAfter(),after(),before()區(qū)別介紹

    jQuery中的insertBefore(),insertAfter(),after(),before()區(qū)別介紹

    這篇文章主要介紹了jQuery中的insertBefore(),insertAfter(),after(),before()區(qū)別介紹的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • jQuery get和post 方法傳值注意事項

    jQuery get和post 方法傳值注意事項

    用 jQuery 的都知道,jQuery 的 get 和 post 方法有三個參數(shù):地址,數(shù)據(jù) 和 回調(diào)函數(shù),但我們知道地址也可以跟隨數(shù)據(jù)的(形如:get_data.php?v1=1&v2=2),而且第二個參數(shù)可以省略,即第二個參數(shù)可以直接寫回調(diào)函數(shù),那么數(shù)據(jù)寫在地址后面和寫在 data 參數(shù)里有什么區(qū)別呢?
    2009-11-11
  • jQuery綁定事件方法及區(qū)別(bind,click,on,live,one)

    jQuery綁定事件方法及區(qū)別(bind,click,on,live,one)

    這篇文章主要介紹了jq綁定事件方法及區(qū)別,通過五種綁定方式使用bind()進行操作,并和one()進行區(qū)分,需要的朋友可以參考下
    2017-08-08
  • jquery lazyload延遲加載技術(shù)的實現(xiàn)原理分析

    jquery lazyload延遲加載技術(shù)的實現(xiàn)原理分析

    懶加載技術(shù)(簡稱lazyload)并不是新技術(shù),它是js程序員對網(wǎng)頁性能優(yōu)化的一種方案。lazyload的核心是按需加載。在大型網(wǎng)站中都有l(wèi)azyload的身影,例如谷歌的圖片搜索頁,迅雷首頁,淘寶網(wǎng),QQ空間等。
    2011-01-01
  • 利用JQuery實現(xiàn)datatables插件的增加和刪除行功能

    利用JQuery實現(xiàn)datatables插件的增加和刪除行功能

    這篇文章給大家介紹了jquery實現(xiàn)datatables插件的增加和刪除行的功能,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的朋友參考下
    2017-01-01
  • jQuery位置選擇器用法實例分析

    jQuery位置選擇器用法實例分析

    這篇文章主要介紹了jQuery位置選擇器用法,結(jié)合實例形式分析了jQuery位置選擇器的功能、用法及相關(guān)操作注意事項,需要的朋友可以參考下
    2019-06-06
  • jQuery實現(xiàn)頁面頂部顯示的進度條效果完整實例

    jQuery實現(xiàn)頁面頂部顯示的進度條效果完整實例

    這篇文章主要介紹了jQuery實現(xiàn)頁面頂部顯示的進度條效果,以完整實例形式分析了jQuery基于animate與setTimeout定時觸發(fā)實現(xiàn)進度條漸進顯示功能,非常簡單實用,需要的朋友可以參考下
    2015-12-12
  • jQuery基于當前元素進行下一步的遍歷

    jQuery基于當前元素進行下一步的遍歷

    通過jQuery方法選中了元素,如何基于這些已經(jīng)選中的元素進行下一步的遍歷呢?下面有個不錯的示例,大家可以參考下
    2014-05-05

最新評論