发布于:代码组织 > Deferreds

jQuery Deferreds

link jQuery Deferreds

Deferreds 是作为 Ajax 模块大规模重写的一部分添加的,该重写由 Julian Aubourg 领导,遵循 CommonJS Promises/A 设计。虽然 1.5 及更高版本包含了 deferred 功能,但以前版本的 jQuery 中的 jQuery.ajax() 接受回调函数,这些回调函数会在请求完成或出错时被调用,但存在重度耦合问题——这与促使使用其他语言或工具包的开发人员选择延迟执行的原则相同。

在实践中,jQuery 版本为你提供了对回调管理方式的几项增强,为你提供了更灵活的方式来提供回调,这些回调可以在原始回调分派已经触发或尚未触发时被调用。还值得注意的是,jQuery 的 Deferred 对象支持将多个回调绑定到特定任务的结果(而不仅仅是一个),其中任务本身可以是同步或异步的。

jQuery 实现的核心是 jQuery.Deferred——一个可链式调用的构造函数,能够创建新的 deferred 对象,这些对象可以检查 promise 是否存在,以确定该对象是否可被观察。它还可以调用回调队列并传递同步和异步函数的成功状态。非常重要的一点是,任何 Deferred 对象的默认状态都是 unresolved(未解决)。通过 .then().fail() 添加到其中的回调会被排队,并在流程稍后执行。

你可以将 Deferred 对象与 when() 的 promise 概念结合使用,在 jQuery 中实现为 $.when(),以等待所有 Deferred 对象的请求完成执行(即,等待所有 promises 被履行)。从技术上讲,$.when() 是一种根据代表异步事件的任意数量的 promises 来执行回调的有效方式。

下面可以看到 $.when() 接受多个参数并与 .then() 结合使用的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
function successFunc() {
console.log( "success!" );
}
function failureFunc() {
console.log( "failure!" );
}
$.when(
$.ajax( "/main.php" ),
$.ajax( "/modules.php" ),
$.ajax( "/lists.php" )
).then( successFunc, failureFunc );

jQuery 中提供的 $.when() 实现非常有趣,因为它不仅解释 deferred 对象,而且当传入的参数不是 deferreds 时,它会将其视为已解决的 deferreds,并立即执行任何回调(doneCallbacks)。还值得注意的是,jQuery 的 deferred 实现除了公开 deferred.then() 之外,还支持 deferred.done()deferred.fail() 方法,这些方法也可以用于向 deferred 的队列添加回调。

现在我们将看一个使用许多 deferred 功能的代码示例。这个非常基本的脚本首先通过 $.get()(它将返回一个类似 promise 的对象)获取 (1) 外部新闻源和 (2) 用于拉取最新评论的反馈源。当两个请求都收到后,会调用 showAjaxedContent() 函数。showAjaxedContent() 函数返回一个 promise,该 promise 在两个容器的动画完成后被解决。当 showAjaxedContent() promise 被解决时,调用 removeActiveClass()removeActiveClass() 返回一个 promise,该 promise 在经过 4 秒后在 setTimeout() 中被解决。最后,在 removeActiveClass() promise 被解决后,如果在此过程中没有发生错误,则调用最后一个 then() 回调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function getLatestNews() {
return $.get( "latestNews.php", function( data ) {
console.log( "news data received" );
$( ".news" ).html( data );
});
}
function getLatestReactions() {
return $.get( "latestReactions.php", function( data ) {
console.log( "reactions data received" );
$( ".reactions" ).html( data );
});
}
function showAjaxedContent() {
// The .promise() is resolved *once*, after all animations complete
return $( ".news, .reactions" ).slideDown( 500, function() {
// Called once *for each element* when animation completes
$(this).addClass( "active" );
}).promise();
}
function removeActiveClass() {
return $.Deferred(function( dfd ) {
setTimeout(function () {
$( ".news, .reactions" ).removeClass( "active" );
dfd.resolve();
}, 4000);
}).promise();
}
$.when(
getLatestNews(),
getLatestReactions()
)
.then(showAjaxedContent)
.then(removeActiveClass)
.then(function() {
console.log( "Requests succeeded and animations completed" );
}).fail(function() {
console.log( "something went wrong!" );
});