概览
$.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 端 | |
| Chrome | 60+ (当前及先前主要版本) |
| Edge | 15+ (基于 Chromium) |
| Firefox | 55+ (当前及先前主要版本) |
| Opera | 47+ (当前及先前主要版本) |
| Safari | 10+ (当前及先前主要版本) |
| 移动端 | |
| Chrome Android | 100+ (当前版本) |
| Firefox for Android | 100+ (当前版本) |
| Opera Android | 64+ (当前版本) |
| Safari on iOS | 10+ |
| Samsung Internet | 6.2+ |
| WebView Android | 100+ (当前版本) |
| WebView on iOS | 10+ |
掌握 $.when() 这一强大的工具,能够有效简化 jQuery多个异步任务 的并发管理。无论是处理多个 Ajax 请求、协调多个动画效果,还是组合其他基于 Promise 的异步操作,$.when() 都提供了清晰且一致的接口。通过合理运用其参数传递和错误处理机制,可以构建出更加健壮和可读性强的异步流程控制逻辑。
