jquery 延迟对象
长期以来,JavaScript开发人员一直使用回调函数来执行多项任务。 一个非常常见的示例是在事件(例如click或keypress )触发时,通过addEventListener()函数添加回调以执行各种操作。 回调函数很简单,可以在简单情况下完成工作。 不幸的是,当您的网页越来越复杂并且您需要并行或顺序执行许多异步操作时,它们将变得无法管理。
ECMAScript 2015(又名ECMAScript 6)引入了一种本地方法来应对这种情况:承诺。 如果您不知道承诺是什么,可以阅读文章JavaScript承诺概述 。 jQuery提供了并且仍然提供了自己的诺言,称为Deferred objects 。 在将承诺引入ECMAScript之前,它们已引入jQuery多年。 在本文中,我将讨论什么是Deferred对象以及它们试图解决的问题。
一个简短的历史
Deferred对象是jQuery 1.5中引入的,它是一个可链接的实用程序,用于将多个回调注册到回调队列中,调用回调队列以及中继任何同步或异步函数的成功或失败状态。 从那时起,它一直是讨论的主题,一些批评和发展中的许多变化。 批评的例子有两个:“ 您错过了承诺的重点”和“ JavaScript的承诺”,以及为什么jQuery的实现被破坏了 。
Deferred与Promise对象一起代表了Promise的jQuery实现。 在jQuery 1.x和2.x版本中, Deferred对象遵循CommonJS Promises / A提议 。 该提议被用作Promise / A +提议的基础,该提议是基于本地承诺的。 如引言中所述,jQuery之所以不遵守Promises / A +提议,是因为它甚至在构想该提议之前就实现了Promise方式。
由于jQuery是先驱,并且由于向后兼容的问题,因此在纯JavaScript以及jQuery 1.x和2.x中使用诺言的方式有所不同。 此外,由于jQuery遵循不同的建议,因此该库与实现了承诺的其他库(例如Q库 )不兼容。
在即将发布的jQuery 3中 ,与本机Promise(在ECMAScript 2015中实现)的互操作性已得到改善 。 由于向后兼容的原因,main方法( then() )的签名仍然有些不同,但是其行为与标准更加一致。
jQuery中的回调
为了理解为什么您可能需要使用Deferred对象,让我们讨论一个示例。 使用jQuery时,通常使用其Ajax方法执行异步请求。 出于示例的原因,假设您正在开发一个将Ajax请求发送到GitHub API的网页。 您的目标是检索用户存储库的列表,找到最近更新的存储库,找到名称中带有字符串“ README.md”的第一个文件,最后检索该文件的内容。 基于此描述,每个Ajax请求都只能在上一步完成时开始。 换句话说,请求必须按顺序运行。
将此描述转换为伪代码(请注意,我没有使用真正的GitHub API),我们得到:
var username = 'testuser';
var fileToSearch = 'README.md';
$.getJSON('https://api.github.com/user/' + username + '/repositories', function(repositories) {
var lastUpdatedRepository = repositories[0].name;
$.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/files', function(files) {
var README = null;
for (var i = 0; i < files.length; i++) {
if (files[i].name.indexOf(fileToSearch) >= 0) {
README = files[i].path;
break;
$.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/file/' + README + '/content', function(content) {
console.log('The content of the file is: ' + content);
});
如本例所示,使用回调,我们必须嵌套调用以按所需顺序执行Ajax请求。 这使代码的可读性降低。 您有许多嵌套的回调或必须同步的独立回调的情况通常称为“回调地狱”。
为了使其更好一点,您可以从我创建的匿名内联函数中提取命名函数。 但是,此更改无济于事,我们仍然处于回调地狱。 输入“ Deferred和“ Promise对象。
延期和承诺对象
在执行异步操作(例如Ajax请求和动画)时,可以使用Deferred对象。 在jQuery中, Promise对象是从Deferred对象或jQuery对象创建的。 它拥有Deferred对象的方法的子集: always() , done() , fail() , state()和then() 。 下一节将介绍这些方法和其他方法。
如果您来自本地JavaScript世界,那么这两个对象的存在可能会让您感到困惑。 当JavaScript具有一个对象( Promise )时,为什么要有两个对象( Deferred和Promise )? 为了解释这些差异及其用例,我将采用与《 jQuery in Action》第三版中所使用的类推相同的类比。
如果编写用于处理异步操作的函数,并且应该返回一个值(也可以是错误或根本没有值),则通常使用Deferred对象。 在这种情况下,您的函数是值的生产者 ,并且您想防止用户更改Deferred的状态。 当您是函数的使用者时 ,将使用promise对象。
为了澄清这个概念,假设您要实现一个基于promise的timeout()函数(我将在本文的下一部分中向您展示该示例的代码)。 您是负责编写必须等待给定时间量的函数的人(在这种情况下,不会返回任何值)。 这使您成为制作人 。 函数的使用者不关心解决或拒绝它。 使用者只需要能够添加功能即可在Deferred的实现,失败或进展时执行。 此外,您想确保使用者无法自行决定解决或拒绝Deferred付款。 为了实现此目标,您需要返回在timeout()函数中创建的Deferred的Promise对象,而不是Deferred本身。 这样,您可以确保除了timeout()函数之外,没有其他人可以调用resolve()或reject()方法。
您可以在此StackOverflow问题中详细了解jQuery的Deferred和Promise对象之间的区别。
现在您知道了这些对象是什么,让我们看一下可用的方法。
递延方法
Deferred对象非常灵活,并提供了满足您所有需求的方法。 可以通过调用jQuery.Deferred()方法来创建它,如下所示:
var deferred = jQuery.Deferred();
或者,使用$快捷方式:
var deferred = $.Deferred();
创建后, Deferred对象将公开几种方法。 忽略那些已弃用或删除的内容,它们是:
-
always(callbacks[, callbacks, ..., callbacks]) :添加要解析或拒绝Deferred对象时要调用的处理程序。
-
done(callbacks[, callbacks, ..., callbacks]) :添加解析Deferred对象时要调用的处理程序。
-
fail(callbacks[, callbacks, ..., callbacks]) :添加拒绝Deferred对象时要调用的处理程序。
-
notify([argument, ..., argument]) :使用给定参数在Deferred对象上调用progressCallbacks 。
-
notifyWith(context[, argument, ..., argument]) :使用给定的上下文和参数在Deferred对象上调用progressCallbacks 。
-
progress(callbacks[, callbacks, ..., callbacks]) :添加Deferred对象生成进度通知时要调用的处理程序。
-
promise([target]) :返回Deferred的Promise对象。
-
reject([argument, ..., argument]) :拒绝一个Deferred对象,并使用给定参数调用任何failCallbacks 。
-
rejectWith(context[, argument, ..., argument]) :拒绝一个Deferred对象,并使用给定的上下文和参数调用所有failCallbacks 。
-
resolve([argument, ..., argument]) :解析一个Deferred对象,并使用给定参数调用任何doneCallbacks 。
-
resolveWith(context[, argument, ..., argument]) :解析一个Deferred对象,并使用给定的上下文和参数调用任何doneCallbacks 。
-
state() :确定Deferred对象的当前状态。
-
then(resolvedCallback[, rejectedCallback[, progressCallback]]) :添加解析,拒绝或仍在处理Deferred对象时要调用的处理程序。
<source type="image/webp"><source><img src="https://s2.51cto.com/images/blog/202312/24205159_6588296f7c66373442.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=" alt="">
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。 原价$ 11.95 您的完全免费
免费获得这本书
这些方法的描述使我有机会强调jQuery文档使用的术语与ECMAScript规范之间的区别。 在ECMAScript规范中,当一个诺言被兑现或被兑现时,它就被说成是可以解决的。 但是,在jQuery的文档中,“解决”一词用于指ECMAScript规范称为实现状态的内容。
由于所提供方法的数量众多,因此本文无法涵盖所有方法。 但是,在接下来的部分中,我将向您展示Deferred和Promise的使用示例。 在第一个示例中,我们将重写在“ jQuery中的回调”一节中检查的代码段,但我们将使用这些对象而不是使用回调。 在第二个示例中,我将阐明所讨论的生产者-消费者类比。
递延的Ajax请求
在本节中,我将展示如何使用Deferred对象及其一些方法来提高“ jQuery回调”部分中开发的代码的可读性。 在深入研究之前,我们必须了解我们需要哪种可用的方法。
根据我们的要求和提供的方法列表,很明显,我们可以使用done()或then()方法来管理成功的案例。 由于你们中的许多人可能已经习惯了JavaScript的Promise对象,因此在此示例中,我将采用then()方法。 这两种方法之间的一个重要区别是, then()能够将作为参数接收的值转发给在then()定义的其他then() , done() , fail()或progress()调用。
最终结果如下所示:
var username = 'testuser';
var fileToSearch = 'README.md';
$.getJSON('https://api.github.com/user/' + username + '/repositories')
.then(function(repositories) {
return repositories[0].name;
.then(function(lastUpdatedRepository) {
return $.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/files');
.then(function(files) {
var README = null;
for (var i = 0; i < files.length; i++) {
if (files[i].name.indexOf(fileToSearch) >= 0) {
README = files[i].path;
break;
return README;
.then(function(README) {
return $.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/file/' + README + '/content');
.then(function(content) {
console.log(content);
});
如您所见,代码具有更高的可读性,因为我们能够按相同级别(就缩进而言)按小步骤破坏整个过程。
创建基于承诺的setTimeout函数
如您所知, setTimeout()是一个在给定时间后执行回调函数的函数。 这两个元素(回调函数和时间)都应作为参数提供。 假设您想在一秒钟后将消息记录到控制台。 通过使用setTimeout()函数,可以使用以下代码实现此目标:
setTimeout(
function() {
console.log('I waited for 1 second!');
);
如您所见,第一个参数是要执行的函数,第二个参数是要等待的毫秒数。 此功能已经运行了好几年,但是如果您需要在Deferred链中引入延迟怎么办?
在下面的代码中,我将向您展示如何使用jQuery提供的Promise对象来开发基于Promise的setTimeout()函数。 为此,我将使用Deferred对象的promise()方法。
最终结果如下所示:
function timeout(milliseconds) {
// Create a new Deferred object
var deferred = $.Deferred();
// Resolve the Deferred after the amount of time specified by milliseconds
setTimeout(deferred.resolve, milliseconds);
// Return the Deferred's Promise object
return deferred.promise();
timeout(1000).then(function() {
console.log('I waited for 1 second!');
});
在此清单中,我定义了一个名为timeout()的函数,该函数包装了JavaScript的本机setTimeout()函数。 在timeout()内部,我创建了一个新的Deferred对象来管理异步任务,该任务包括在指定的毫秒数后解析Deferred对象。 在这种情况下, timeout()函数是值的生产者,因此它将创建Deferred对象并返回Promise对象。 通过这样做,我确保函数的调用方(使用者)不能随意解析或拒绝Deferred对象。 实际上,调用者只能使用诸如done()和fail()方法添加要执行的函数。
jQuery 1.x / 2.x和jQuery 3之间的区别
在使用Deferred的第一个示例中,我们开发了一个片段,该片段寻找名称中包含字符串“ README.md”的文件,但我们没有考虑找不到该文件的情况。 这种情况可以视为失败。 发生这种情况时,我们可能希望中断通话链,直接跳到结尾。 为此,就像使用JavaScript的catch()方法一样,自然会引发异常并使用fail()方法捕获它。
在Promises / A和Promises / A +兼容库(例如jQuery 3.x)中,将引发的异常转换为拒绝,并调用失败回调,例如添加了fail()的失败回调。 这将异常作为参数接收。
在jQuery 1.x和2.x中,未捕获的异常将暂停程序的执行。 这些版本允许抛出的异常冒泡,通常到达window.onerror 。 如果未定义任何函数来处理此异常,则会显示该异常的消息,并中止程序的执行。
为了更好地理解不同的行为,请看一下我的书中的以下示例:
var deferred = $.Deferred();
deferred
.then(function() {
throw new Error('An error message');
.then(
function() {
console.log('First success function');
function() {
console.log('First failure function');
.then(
function() {
console.log('Second success function');
function() {
console.log('Second failure function');
deferred.resolve();
在jQuery 3.x中,此代码会将消息“第一个失败功能”和“第二个成功功能”写入控制台。 原因是,正如我之前提到的,规范指出应将抛出的异常转换为拒绝,并且必须使用该异常调用失败回调。 另外,一旦异常被处理(在我们的示例中,失败回调传递给第二个then() ),就应该执行以下成功函数(在这种情况下,成功回调传递给第三个then() )。
在jQuery 1.x和2.x中,仅执行第一个函数(抛出错误的函数),并且仅会在控制台上显示消息“未捕获的错误:错误消息”。
jQuery 1.x / 2.x
jsbin.com上的JS Bin
jQuery 3
jsbin.com上的JS Bin
为了进一步提高与ECMAScript 2015的兼容性,jQuery 3还向Deferred和Promise对象添加了一个名为catch()的新方法。 这是一种定义处理程序的方法,该处理程序在Deferred对象被rejected或其Promise对象处于拒绝状态时执行。 其签名如下:
deferred.catch(rejectedCallback)
此方法不过是then(null, rejectedCallback)的快捷方式。
结论
在本文中,我向您介绍了jQuery的Promise实现。 Promise允许您避免讨厌的技巧来同步并行异步函数,以及避免将回调嵌套在回调内部的回调中……
除了显示一些示例外,我还介绍了jQuery 3如何通过本机Promise改进互操作性。 尽管旧版jQuery和ECMAScript 2015之间突出显示了差异,但Deferred仍然是工具箱中非常强大的工具。 作为专业开发人员,并且随着项目难度的增加,您会发现自己经常使用它。
翻译自: https://www.sitepoint.com/introduction-jquery-deferred-objects/
jquery 延迟对象