主页/jQuery教程/回调、延迟与异步/jQuery管理多个异步任务:$.when() 的用法

jQuery管理多个异步任务:$.when() 的用法

8,614字
36–55 分钟

概览

目录

$.when() 是 jQuery 中用于协调和管理多个异步任务的工具函数。它提供了一种基于 Deferred(延迟对象)的机制,允许在一个回调函数中等待多个异步操作(如 Ajax 请求、动画或自定义延迟任务)全部完成,然后执行相应的逻辑。通过 $.when(),可以优雅地处理 jQuery多个异步任务 的并发场景,避免嵌套过深的回调函数,提升代码的可读性和可维护性。

$.when() 基础

语法

jQuery.when( deferreds )

deferreds:零个、一个或多个延迟对象(Deferred 对象),或普通的 JavaScript 对象。

返回值

返回一个只读的 Promise 对象,用于添加回调函数。

无参数调用

当不传递任何参数时,$.when() 返回一个已经 resolved(已解决)的 Promise。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when 无参数示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="checkBtn">检查状态</button>
    <div id="result"></div>

    <script>
        $('#checkBtn').on('click', function() {
            $.when().then(function() {
                $('#result').text('$.when() 无参数调用,立即执行');
            });
        });
    </script>
</body>
</html>

单个延迟对象

传递一个延迟对象时,$.when() 返回的 Promise 会跟随该延迟对象的状态变化。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when 单个延迟对象示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="startBtn">发起单个 Ajax 请求</button>
    <div id="output"></div>

    <script>
        $('#startBtn').on('click', function() {
            $.when($.ajax('https://api.github.com/users/jquery'))
                .done(function(data) {
                    $('#output').html('<p>请求成功: ' + data.name + '</p>');
                })
                .fail(function() {
                    $('#output').html('<p>请求失败</p>');
                });
        });
    </script>
</body>
</html>

多个延迟对象

传递多个延迟对象时,$.when() 会等待所有延迟对象都完成(resolved 或 rejected)。只有当所有延迟对象都成功解决时,done 回调才会执行;只要有一个失败,fail 回调就会执行。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when 多个延迟对象示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="startMultipleBtn">发起多个请求</button>
    <div id="output"></div>

    <script>
        $('#startMultipleBtn').on('click', function() {
            var request1 = $.ajax('https://api.github.com/users/jquery');
            var request2 = $.ajax('https://api.github.com/users/twitter');
            var request3 = $.ajax('https://api.github.com/users/microsoft');

            $.when(request1, request2, request3)
                .done(function(resp1, resp2, resp3) {
                    $('#output').html(`
                        <p>所有请求成功</p>
                        <p>用户1: ${resp1[0].name}</p>
                        <p>用户2: ${resp2[0].name}</p>
                        <p>用户3: ${resp3[0].name}</p>
                    `);
                })
                .fail(function() {
                    $('#output').html('<p>至少有一个请求失败</p>');
                });
        });
    </script>
</body>
</html>

与非延迟对象混合

$.when() 可以同时接收延迟对象和普通值。普通值会被自动视为已解决的延迟对象,参数在回调中保持不变。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when 混合参数示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="mixedBtn">混合参数</button>
    <div id="output"></div>

    <script>
        $('#mixedBtn').on('click', function() {
            var ajaxRequest = $.ajax('https://api.github.com/users/jquery');
            var staticValue = '静态数据';
            var anotherValue = 123;

            $.when(ajaxRequest, staticValue, anotherValue)
                .done(function(ajaxResp, str, num) {
                    $('#output').html(`
                        <p>Ajax 响应: ${ajaxResp[0].name}</p>
                        <p>字符串: ${str}</p>
                        <p>数字: ${num}</p>
                    `);
                });
        });
    </script>
</body>
</html>

参数处理

单个参数

当只有一个参数且为延迟对象时,done 回调接收到的是该延迟对象传递的参数(通常是一个数组,包含响应数据、状态字符串和 jqXHR 对象)。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when 单个参数示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="singleParamBtn">单个参数</button>
    <div id="output"></div>

    <script>
        $('#singleParamBtn').on('click', function() {
            $.when($.ajax('https://api.github.com/users/jquery'))
                .done(function(data, textStatus, jqXHR) {
                    $('#output').html(`
                        <p>数据: ${data.name}</p>
                        <p>状态: ${textStatus}</p>
                    `);
                });
        });
    </script>
</body>
</html>

多个参数

当有多个参数时,done 回调接收到的每个参数分别对应每个延迟对象传递的参数列表。每个参数本身是一个数组,包含该延迟对象传递给回调的值。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when 多参数结构示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="multiParamBtn">多参数结构</button>
    <div id="output"></div>

    <script>
        $('#multiParamBtn').on('click', function() {
            var req1 = $.ajax('https://api.github.com/users/jquery');
            var req2 = $.ajax('https://api.github.com/users/twitter');

            $.when(req1, req2).done(function(resp1, resp2) {
                // resp1 是一个数组: [data1, statusText1, jqXHR1]
                // resp2 是一个数组: [data2, statusText2, jqXHR2]
                $('#output').html(`
                    <p>第一个响应: ${resp1[0].name}</p>
                    <p>第二个响应: ${resp2[0].name}</p>
                `);
            });
        });
    </script>
</body>
</html>

错误处理

处理部分失败

当多个延迟对象中有任何一个失败时,$.when() 立即触发 fail 回调。可以通过 then 方法同时处理成功和失败情况。

示例

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when 错误处理示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="errorHandleBtn">测试错误处理</button>
    <div id="output"></div>

    <script>
        $('#errorHandleBtn').on('click', function() {
            var validReq = $.ajax('https://api.github.com/users/jquery');
            var invalidReq = $.ajax('https://api.github.com/users/non-existent-user-12345');

            $.when(validReq, invalidReq)
                .then(
                    function() {
                        $('#output').html('<p>所有请求成功(这不会执行)</p>');
                    },
                    function() {
                        $('#output').html('<p>至少一个请求失败,执行失败回调</p>');
                    }
                );
        });
    </script>
</body>
</html>

实际应用场景

场景一:等待多个资源加载

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>等待多个资源加载</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <style>
        .loading { color: gray; }
        .loaded { color: green; font-weight: bold; }
    </style>
</head>
<body>
    <div id="status">正在加载资源...</div>
    <div id="content"></div>

    <script>
        function loadResource(url) {
            return $.ajax(url).then(function(data) {
                return data;
            });
        }

        var resources = [
            loadResource('https://api.github.com/users/jquery'),
            loadResource('https://api.github.com/users/twitter'),
            loadResource('https://api.github.com/users/microsoft')
        ];

        $.when.apply($, resources)
            .done(function() {
                var args = Array.prototype.slice.call(arguments);
                $('#status').text('所有资源加载完成').addClass('loaded');
                $.each(args, function(index, resp) {
                    $('#content').append('<p>' + resp[0].login + '</p>');
                });
            })
            .fail(function() {
                $('#status').text('部分资源加载失败').css('color', 'red');
            });
    </script>
</body>
</html>

场景二:组合动画效果

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>组合动画示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <style>
        .box { width: 100px; height: 100px; margin: 10px; background-color: blue; display: inline-block; }
    </style>
</head>
<body>
    <div class="box" id="box1"></div>
    <div class="box" id="box2"></div>
    <div class="box" id="box3"></div>
    <button id="startAnimBtn">开始所有动画</button>
    <div id="animStatus"></div>

    <script>
        $('#startAnimBtn').on('click', function() {
            var anim1 = $('#box1').fadeOut(1000).promise();
            var anim2 = $('#box2').slideUp(1000).promise();
            var anim3 = $('#box3').fadeTo(1000, 0.2).promise();

            $.when(anim1, anim2, anim3)
                .done(function() {
                    $('#animStatus').text('所有动画完成').css('color', 'green');
                })
                .fail(function() {
                    $('#animStatus').text('动画执行出错').css('color', 'red');
                });
        });
    </script>
</body>
</html>

场景三:动态加载脚本并执行

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>动态加载脚本示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <button id="loadScriptsBtn">加载所有脚本</button>
    <div id="scriptStatus"></div>

    <script>
        $('#loadScriptsBtn').on('click', function() {
            var script1 = $.getScript('https://code.jquery.com/ui/1.13.2/jquery-ui.min.js');
            var script2 = $.getScript('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js');

            $.when(script1, script2)
                .done(function() {
                    $('#scriptStatus').text('所有脚本加载完成,可以使用 jQuery UI 和 moment.js');
                })
                .fail(function() {
                    $('#scriptStatus').text('脚本加载失败').css('color', 'red');
                });
        });
    </script>
</body>
</html>

场景四:使用 apply 处理动态数量的延迟对象

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>$.when.apply 示例</title>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
    <input type="text" id="usernameInput" placeholder="输入 GitHub 用户名,逗号分隔">
    <button id="fetchUsersBtn">获取用户信息</button>
    <div id="usersOutput"></div>

    <script>
        $('#fetchUsersBtn').on('click', function() {
            var input = $('#usernameInput').val();
            var usernames = input.split(',').map(function(s) { return s.trim(); });
            var requests = [];

            $.each(usernames, function(index, username) {
                if (username) {
                    requests.push($.ajax('https://api.github.com/users/' + username));
                }
            });

            if (requests.length === 0) {
                $('#usersOutput').text('请输入有效用户名');
                return;
            }

            $.when.apply($, requests)
                .done(function() {
                    var args = arguments;
                    var html = '<p>获取到以下用户信息:</p><ul>';
                    for (var i = 0; i < args.length; i++) {
                        html += '<li>' + args[i][0].login + ': ' + args[i][0].name + '</li>';
                    }
                    html += '</ul>';
                    $('#usersOutput').html(html);
                })
                .fail(function() {
                    $('#usersOutput').text('部分用户信息获取失败').css('color', 'red');
                });
        });
    </script>
</body>
</html>

版本变更记录

版本变更说明
1.5引入 jQuery.when() 方法,作为 Deferred 系统的一部分,提供多个异步任务的协调能力。
1.8改进了与非延迟对象混合使用时的参数传递行为,使普通值在回调中保持原样传递。
1.12/2.2增强了错误处理机制,使得 fail 回调能够更准确地反映失败来源。
3.0优化了内部实现,提高了与原生 Promise 的互操作性。$.when 的返回对象更接近标准的 Promise 行为。
4.0从“slim”构建版本中移除了 $.when(连同 $.Deferred$.Callbacks)以减小体积。完整构建版本中依然保留,功能不变。若需要 IE11 支持,应使用完整构建版本。

浏览器兼容性

基于 jQuery 3.x 或 4.x 版本,$.when() 的兼容性与 jQuery 核心库一致。对于 jQuery 4.x,已移除对 IE 10 及以下版本的支持。

浏览器类型最低兼容版本(基于 jQuery 3.x/4.x)
PC 端
Chrome60+ (当前及先前主要版本)
Edge15+ (基于 Chromium)
Firefox55+ (当前及先前主要版本)
Opera47+ (当前及先前主要版本)
Safari10+ (当前及先前主要版本)
移动端
Chrome Android100+ (当前版本)
Firefox for Android100+ (当前版本)
Opera Android64+ (当前版本)
Safari on iOS10+
Samsung Internet6.2+
WebView Android100+ (当前版本)
WebView on iOS10+

掌握 $.when() 这一强大的工具,能够有效简化 jQuery多个异步任务 的并发管理。无论是处理多个 Ajax 请求、协调多个动画效果,还是组合其他基于 Promise 的异步操作,$.when() 都提供了清晰且一致的接口。通过合理运用其参数传递和错误处理机制,可以构建出更加健壮和可读性强的异步流程控制逻辑。