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

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

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

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

$.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().


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

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


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

// deferred對(duì)象所有的方法數(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()返回的對(duì)象包含resolve(), resolveWith(),可能會(huì)產(chǎn)生哪些影響呢?
我們還是用例子也說(shuō)明,首先看看erichynds原文的第一個(gè)例子:
復(fù)制代碼 代碼如下:

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

// 添加另外一個(gè)AJAX回調(diào)函數(shù),此時(shí)AJAX或許已經(jīng)結(jié)束,或許還沒(méi)有結(jié)束
// 由于$.ajax內(nèi)置了deferred的支持,所以我們可以這樣寫(xiě)
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源代碼中沒(méi)有
jqXHR.resolve = deferred.resolve;
jqXHR.resolveWith = deferred.resolveWith;

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

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

req.resolve();

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

console.log('END');

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

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

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

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

req.resolve('Fake data');

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

console.log('END');

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

代碼分析
在深入jQuery代碼之前,先來(lái)看看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.

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

至此,大家對(duì)promise應(yīng)該有清晰的認(rèn)識(shí)了。我們?cè)賮?lái)看下面兩段代碼,它們完成的功能完全一致:
復(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ù),并且似乎第二段代碼更加簡(jiǎn)潔,但是第二段代碼卻不是推薦的做法。
因?yàn)槿蝿?wù)(showDiv)本身狀態(tài)的更改應(yīng)該保持在任務(wù)內(nèi)部,而不需要對(duì)外公開(kāi),對(duì)外只需要公開(kāi)一個(gè)promise的只讀deferred對(duì)象就行了。

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

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

jQuery.extend(
// 完備的deferred對(duì)象(具有兩個(gè)回調(diào)隊(duì)列)
Deferred: function (func) {
var deferred = jQuery._Deferred(),
failDeferred = jQuery._Deferred(),
promise;
// 添加then, promise 以及出錯(cuò)相關(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對(duì)象的只讀副本
// 如果將obj作為參數(shù)傳遞進(jìn)去,則promise相關(guān)方法將會(huì)添加到這個(gè)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;
}
});
// 確保只有一個(gè)回調(diào)函數(shù)隊(duì)列可用,也就是說(shuō)一個(gè)任務(wù)要么成功,要么失敗
deferred.done(failDeferred.cancel).fail(deferred.cancel);
// 刪除cancel函數(shù)
delete deferred.cancel;
// 將當(dāng)前創(chuàng)建的作為參數(shù)傳遞到給定的函數(shù)中
if (func) {
func.call(deferred, deferred);
}
return deferred;
});

如果你覺(jué)得上面的代碼閱讀比較困難,沒(méi)關(guān)系我寫(xiě)了一個(gè)簡(jiǎ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;
}

上面代碼定義了一個(gè)Arr,用來(lái)生成一個(gè)數(shù)組對(duì)象,包含一些方法,比如add(), length(), clear(), promise()。
其中promise()返回當(dāng)前Arr對(duì)象的一個(gè)副本,只能向其中添加元素,而不能清空內(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()
還記得前面提到的那兩個(gè)完成相同功能的代碼么?
復(fù)制代碼 代碼如下:

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

function showDiv() {
// 這里返回promise()或者直接返回deferred對(duì)象,代碼都能正確運(yùn)行。
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!');
});

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

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

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

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

那么是不是說(shuō):$.Deferred().promise() === $.Deferred().promise().promise()
我們還是通過(guò)示例來(lái)驗(yàn)證我們的想法:
復(fù)制代碼 代碼如下:

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

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

promise: function (obj) {
if (obj == null) {
// 在這里,如果promise已經(jīng)存在(已經(jīng)調(diào)用過(guò).promise()),就不會(huì)重新創(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對(duì)象的只讀屬性。
2. 建議任務(wù)不要返回deferred對(duì)象,而是返回deferred.promise()對(duì)象。這樣外部就不能隨意更改任務(wù)的內(nèi)部流程。
3. deferred.promise() === deferred.promise().promise() (上面我們分別從代碼推理,和源代碼分析兩個(gè)角度得到這個(gè)結(jié)論)

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

相關(guān)文章

最新評(píng)論