主页/jQuery教程/回调、延迟与异步/jQuery只读视图:Promise对象与 .promise() 方法

jQuery只读视图:Promise对象与 .promise() 方法

5,043字
21–32 分钟

概览

目录

在现代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 所支持的最低浏览器版本,在这些环境中,上述异步流程控制方法能够正常工作。

浏览器最低支持版本
Chrome30+
Edge12+
Firefox25+
Opera18+
Safari7+
Chrome Android30+
Firefox for Android25+
Opera Android18+
Safari on iOS7+
Samsung Internet4.0+
WebView Android4.4+
WebView on iOS7+

:上述版本基于 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 异步编程模型的核心。