概览
在现代JavaScript异步编程中,Promise 是一种核心的抽象概念。jQuery 通过其延迟对象(Deferred)体系,提供了一套完整的 Promise 实现。其中,jQuery Promise对象 是由 .promise() 方法返回的一种只读视图。它本质上是一个受限制的 Deferred 对象,允许添加回调函数来观察异步操作的状态(已完成或失败),但无法主动更改该状态。理解 jQuery .promise() 方法 及其返回的 jQuery只读视图,是掌握 jQuery 中动画队列、集合操作乃至复杂异步流程控制的关键。
.promise() 方法
.promise() 方法通常作用于一个 jQuery 集合,返回一个 Promise 对象,用于观察该集合上特定类型操作(主要是动画)的完成情况。当集合中的所有匹配元素完成了指定类型的操作(如动画队列清空)时,该 Promise 对象将被解决(resolved)。
语法
.promise( [type ] [, target ] )参数说明
type(可选):一个字符串,指定需要观察的队列类型。默认值是"fx",代表标准的动画队列。可以指定其他自定义队列名称。target(可选):一个对象,Promise 将被合并到此对象上,并返回该对象。这允许将 Promise 行为添加到已有的对象上。
返回值
返回一个 jQuery Promise对象。该对象包含了 .done()、.fail()、.always()、.then() 等用于绑定回调的方法,但缺少 Deferred 对象的 .resolve()、.reject() 等改变状态的方法,因此是只读的。
示例:观察动画队列完成
以下示例展示了如何使用 .promise() 在多个元素的动画全部结束后执行回调。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>jQuery .promise() 方法示例:动画完成通知</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style>
.box {
width: 100px;
height: 100px;
background-color: blue;
margin: 10px;
display: inline-block;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<button id="startBtn">开始动画</button>
<p id="status"></p>
<script>
$(document).ready(function() {
$('#startBtn').on('click', function() {
$('#status').text('动画进行中...');
// 对所有的 .box 元素执行一个滑上和滑下动画
$('.box').slideUp(1000).slideDown(1000);
// 创建一个 Promise 来观察这些元素的 'fx' 队列
$('.box').promise().done(function() {
$('#status').text('所有动画已完成!');
});
});
});
</script>
</body>
</html>Promise对象与Deferred的关系
jQuery Promise对象 和 Deferred 对象共同构成了 jQuery 的异步编程模型。Deferred 对象可以理解为一个状态机的控制器,它拥有 .resolve() 和 .reject() 等方法来主动触发状态变更。而 jQuery Promise对象 则是从 Deferred 对象上通过 .promise() 方法获取的 只读视图。
| 特性 | Deferred 对象 | Promise 对象 (只读视图) |
|---|---|---|
| 状态变更 | 可以主动触发 (.resolve(), .reject()) | 不能主动触发,状态只由对应的 Deferred 控制 |
| 添加回调 | 支持 (.done(), .fail(), .always(), .then()) | 支持 (.done(), .fail(), .always(), .then()) |
| 主要用途 | 内部控制异步操作的状态,生产 Promise | 对外提供观察异步结果的安全接口 |
这种分离确保了异步结果的“唯一决议方”是产生它的 Deferred 对象,任何通过 Promise 对象获取结果的代码都无法意外地改变其状态,从而避免了状态管理的混乱。
将动画与Ajax转化为Promise
jQuery 的许多操作都可以转化为 Promise,从而实现更灵活的异步流程控制。
动画的 Promise
如前所述,.promise() 方法天然地为动画队列提供了 Promise 接口。这使得将多个动画串联或并行执行,并在所有动画完成后执行逻辑变得异常简单。
Ajax 的 Promise
从 jQuery 1.5 开始,$.ajax() 返回的对象就是一个 jQuery Promise对象(实际上是 jqXHR 对象,它实现了 Promise 接口)。这意味着可以直接在 Ajax 请求上链式调用 .done()、.fail() 等方法来处理响应。
示例:使用 Promise 风格的 Ajax
以下示例展示了如何使用 $.ajax() 返回的 Promise 对象处理成功和失败的回调。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>jQuery Promise 示例:Ajax 请求</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
<button id="loadData">加载数据</button>
<div id="result"></div>
<script>
$(document).ready(function() {
$('#loadData').on('click', function() {
var $result = $('#result').text('请求中...');
// $.ajax 返回一个 Promise 兼容的对象
$.ajax({
url: 'https://api.github.com/users/jquery/repos',
dataType: 'jsonp' // 使用 JSONP 进行跨域示例
})
.done(function(data) {
var repos = data.data;
if (repos && repos.length) {
$result.html('第一个仓库名称:' + repos[0].name);
} else {
$result.text('未获取到仓库信息。');
}
})
.fail(function(jqXHR, textStatus) {
$result.text('请求失败:' + textStatus);
});
});
});
</script>
</body>
</html>管理多个异步任务:$.when()
结合 jQuery Promise对象 和 $.when() 方法,可以优雅地等待多个异步任务(如多个 Ajax 请求或动画)全部完成后执行特定逻辑。
语法
$.when( deferreds )参数说明
deferreds:一个或多个 Deferred 对象(或 Promise 对象),或者普通的 JavaScript 对象。
返回值
返回一个 jQuery Promise对象。当所有传入的 Deferred 对象都解决时,该 Promise 被解决;如果任一 Deferred 被拒绝,则该 Promise 被拒绝。
示例:等待多个 Ajax 请求完成
以下示例同时发起两个 GitHub API 请求,并在两者都成功后合并处理结果。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>jQuery $.when() 示例:多任务同步</title>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
<button id="loadBoth">加载两个用户</button>
<div id="output"></div>
<script>
$(document).ready(function() {
$('#loadBoth').on('click', function() {
var $output = $('#output').text('加载中...');
var request1 = $.ajax({
url: 'https://api.github.com/users/jquery',
dataType: 'jsonp'
});
var request2 = $.ajax({
url: 'https://api.github.com/users/twitter',
dataType: 'jsonp'
});
// 等待两个请求都完成
$.when(request1, request2).done(function(resp1, resp2) {
var user1 = resp1[0].data; // 注意 jsonp 返回的数组结构
var user2 = resp2[0].data;
$output.html(
'<p>第一个用户:' + user1.name + ' (粉丝:' + user1.followers + ')</p>' +
'<p>第二个用户:' + user2.name + ' (粉丝:' + user2.followers + ')</p>'
);
}).fail(function() {
$output.text('至少一个请求失败。');
});
});
});
</script>
</body>
</html>版本变更记录
下表梳理了jQuery版本迭代中与 Promise 和 .promise() 方法相关的重要变更。
| 版本 | 变更内容与影响 |
|---|---|
| 1.5 | 引入了 Deferred 对象和 Promise 概念。$.ajax() 开始返回 jQuery Promise对象(jqXHR)。 |
| 1.6 | 增加了 .promise() 方法,允许为集合上的动画队列创建 Promise。 |
| 1.8 | .deferred.pipe() 方法被废弃,推荐使用 .then() 进行 Promise 的链式过滤和链接。 |
| 3.0 | 更新了 Promise 的行为以更好地兼容 Promises/A+ 规范。例如,由 .then() 创建的新 Promise 的异常处理方式更加符合标准。 |
| 4.0 | 重大变更:在“slim”构建版本中移除了 Deferred 和 Callbacks 模块,以减小体积。这意味着如果使用 slim 版本,.promise()、$.Deferred、$.when 等方法将不可用。官方建议在大多数场景下使用原生 Promise 替代。对于需要支持 IE11 或依赖特定 Deferred 功能的项目,应使用完整构建版本。 |
浏览器兼容状态
.promise() 方法和 jQuery Promise对象 作为 jQuery 核心功能的一部分,其兼容性与 jQuery 库本身保持一致。下表列出了 jQuery 所支持的最低浏览器版本,在这些环境中,上述异步流程控制方法能够正常工作。
| 浏览器 | 最低支持版本 |
|---|---|
| Chrome | 30+ |
| Edge | 12+ |
| Firefox | 25+ |
| Opera | 18+ |
| Safari | 7+ |
| Chrome Android | 30+ |
| Firefox for Android | 25+ |
| Opera Android | 18+ |
| Safari on iOS | 7+ |
| Samsung Internet | 4.0+ |
| WebView Android | 4.4+ |
| WebView on iOS | 7+ |
注:上述版本基于 jQuery 3.x 系列的兼容性测试。对于 jQuery 4.0.0 的 slim 构建版本,由于移除了 Deferred 模块,jQuery Promise对象 和
.promise()方法将不可用。开发者若需使用这些功能,应选用 jQuery 4.0.0 的完整构建版本,或迁移至原生 Promise。在更早的浏览器环境(如 Internet Explorer 6-8)中,可以使用 jQuery 1.x 系列,该系列同样支持 Promise 机制,但 API 行为可能存在细微差异。理解 jQuery只读视图 的概念及其与 Deferred 的关系,是驾驭 jQuery 异步编程模型的核心。
