在jQuery1.5中使用deferred对象 着放大镜看Promise(2)


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

req.resolve('Fake data');

// 添加另外一个AJAX回调函数,此时AJAX或许已经结束,或许还没有结束
// 由于$.ajax内置了deferred的支持,所以我们可以这样写
req.success(function (response) {
console.log('AJAX success2(' + response + ')');
});

console.log('END');


此时的执行结果为:
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()用来阻止其它代码修改异步任务的内部流程。Promise的对象只对外公开添加回调函数和检测状态的函数,而不包含修改状态的函数。
如果你手工创建了一个deferred对象,那么你要维持对这个deferred对象的引用,以此来修改状态触发回调函数。不过你的返回值应该是deferred.promise(),这样外部程序可以添加回调函数或检测状态,而不能修改状态。

至此,大家对promise应该有清晰的认识了。我们再来看下面两段代码,它们完成的功能完全一致:

复制代码 代码如下:


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!');
});


复制代码 代码如下:


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!');
});


虽然上面两段代码完成相同的任务,并且似乎第二段代码更加简洁,但是第二段代码却不是推荐的做法。
因为任务(showDiv)本身状态的更改应该保持在任务内部,而不需要对外公开,对外只需要公开一个promise的只读deferred对象就行了。

最后,我们来看看Deferred相关源代码:

复制代码 代码如下:


// Promise相关方法数组
promiseMethods = "then done fail isResolved isRejected promise".split( " " ),

jQuery.extend(
// 完备的deferred对象(具有两个回调队列)
Deferred: function (func) {
var deferred = jQuery._Deferred(),
failDeferred = jQuery._Deferred(),
promise;
// 添加then, promise 以及出错相关的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作为参数传递进去,则promise相关方法将会添加到这个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;
}
});
// 确保只有一个回调函数队列可用,也就是说一个任务要么成功,要么失败
deferred.done(failDeferred.cancel).fail(deferred.cancel);
// 删除cancel函数
delete deferred.cancel;
// 将当前创建的作为参数传递到给定的函数中
if (func) {
func.call(deferred, deferred);
}
return deferred;
});


如果你觉得上面的代码阅读比较困难,没关系我写了一个简单的类似代码:

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdfdxf.html