发布在:代码组织 > Deferred

jQuery Deferred

link jQuery Deferred

Deferred 是作为 Ajax 模块大规模重写的一部分添加的,由 Julian Aubourg 领导,遵循 CommonJS Promises/A 设计。虽然 1.5 及以上版本包含 deferred 功能,但以前版本的 jQuery 接受了在请求完成或出错时调用的回调的 jQuery.ajax(),但遭受了严重的耦合——同样的原则会驱使使用其他语言或工具包的开发人员选择延迟执行。

实际上,jQuery 的版本为您提供了回调管理方式的若干增强功能,为您提供了更灵活的方法来提供回调,无论原始回调分派是否已触发。还值得注意的是,jQuery 的 Deferred 对象支持将多个回调绑定到特定任务的结果(而不仅仅是一个),而任务本身可以是同步的或异步的。

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

您可以将 Deferred 对象与 jQuery 中作为 $.when() 实现的 when() 承诺概念结合使用,以等待所有 Deferred 对象的请求完成执行(即所有承诺都已兑现)。从技术角度讲,$.when() 实际上是一种基于表示异步事件的任意数量的承诺执行回调的方式。

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

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() 实现非常有趣,因为它不仅解释了延迟对象,而且在传递不是延迟的参数时,它会将这些参数视为已解决的延迟,并立即执行任何回调(doneCallbacks)。还值得注意的是,除了公开 deferred.then() 之外,jQuery 的延迟实现还支持 deferred.done()deferred.fail() 方法,这些方法也可用于向延迟队列添加回调。

现在,我们将看一个使用许多延迟功能的代码示例。这个非常基本的脚本首先使用 $.get()(它将返回一个类似承诺的对象)来使用(1)外部新闻提要和(2)反应提要来提取最新评论。收到这两个请求后,将调用 showAjaxedContent() 函数。showAjaxedContent() 函数返回一个承诺,当两个容器的动画完成时,该承诺将得到解决。当 showAjaxedContent() 承诺得到解决时,将调用 removeActiveClass()removeActiveClass() 返回一个承诺,该承诺在经过 4 秒后在 setTimeout() 内得到解决。最后,在 removeActiveClass() 承诺得到解决后,将调用最后一个 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!" );
});