什么是 jQuery deferred.progress() 方法
在前端开发中,异步操作是家常便饭。无论是加载图片、请求数据,还是执行复杂的动画,我们都需要处理“等一等”的场景。jQuery 提供了一套优雅的异步处理机制,其中 deferred 对象就是核心工具之一。而 deferred.progress() 方法,正是这套机制中一个容易被忽视却非常实用的功能。
想象一下你正在下载一个大文件。浏览器不会立刻告诉你“完成了”,而是会持续告诉你“已经下载了 30%”。这个“进度提示”就是 progress() 方法的用武之地。它允许你在异步任务进行中,实时接收中间状态信息,而不是只能等到最终成功或失败。
jQuery deferred.progress() 方法用于注册一个或多个回调函数,这些函数会在异步操作的“进度更新”事件触发时被调用。它特别适合需要显示加载进度、执行长时间任务的场景。与 done() 和 fail() 不同,progress() 是在任务“进行中”时被触发,而不是在结束时。
这个方法的出现,让 jQuery 的异步编程能力从“要么成功,要么失败”进化到了“正在进行中,还有多少进度”。对于提升用户体验来说,这一步非常关键。
如何使用 deferred.progress() 方法
使用 deferred.progress() 方法其实非常直观。它接收一个函数作为参数,这个函数会在每次进度更新时被调用。这个函数会接收到一个表示进度的参数,通常是 0 到 1 之间的数字,代表完成比例。
下面是一个基础示例:
// 创建一个 deferred 对象
var deferred = $.Deferred();
// 注册 progress 回调
deferred.progress(function(percentage) {
console.log('当前进度:' + (percentage * 100).toFixed(2) + '%');
});
// 模拟异步任务,每隔 100 毫秒更新一次进度
var interval = setInterval(function() {
var progress = deferred.state() === 'pending' ? Math.random() : 1;
deferred.notify(progress);
if (progress === 1) {
clearInterval(interval);
deferred.resolve(); // 任务完成
}
}, 100);
这段代码中:
$.Deferred()创建了一个异步任务容器,相当于一个“任务管理器”。deferred.progress()注册了一个回调函数,用于接收进度信息。deferred.notify()是触发进度更新的关键方法,它会调用所有已注册的progress回调。Math.random()模拟了真实任务中不确定的进度增长。clearInterval和deferred.resolve()用于在任务完成后终止模拟并标记完成。
注意:notify() 是触发 progress() 回调的唯一方式。没有 notify(),progress() 就不会被调用。
与 done 和 fail 的区别与协作
在理解 deferred.progress() 之前,我们先回顾一下 done() 和 fail() 的作用。done() 在任务成功时被调用,fail() 在任务失败时被调用。它们是“终点”的回调。
而 progress() 是“途中”的回调。三者共同构成了完整的异步流程控制:
done():任务成功完成,执行最终操作。fail():任务失败,执行错误处理。progress():任务进行中,提供实时反馈。
这种分工让代码逻辑更清晰。例如在上传文件的场景中:
var uploadDeferred = $.Deferred();
// 进度回调:更新进度条
uploadDeferred.progress(function(percentage) {
$('#progress-bar').css('width', (percentage * 100) + '%');
$('#progress-text').text(Math.round(percentage * 100) + '%');
});
// 成功回调:上传完成,显示成功消息
uploadDeferred.done(function(response) {
alert('上传成功!' + response.message);
});
// 失败回调:上传失败,显示错误信息
uploadDeferred.fail(function(error) {
alert('上传失败:' + error.message);
});
// 模拟上传过程
function simulateUpload() {
var progress = 0;
var interval = setInterval(function() {
progress += 0.1; // 每次增加 10%
uploadDeferred.notify(progress);
if (progress >= 1) {
clearInterval(interval);
uploadDeferred.resolve({ message: '文件已上传' });
}
}, 500);
}
// 启动上传
simulateUpload();
在这个例子中,我们同时使用了三种回调:
progress()实时更新 UI。done()处理成功结果。fail()处理异常情况。
三者互不干扰,各自负责不同阶段,让代码结构更清晰,维护性更强。
实际应用场景:文件上传进度条
文件上传是 deferred.progress() 方法最典型的应用场景。浏览器原生的 XMLHttpRequest 支持 progress 事件,但封装成 jQuery 的 deferred 模式后,可以更统一地处理各种异步操作。
下面是一个完整的文件上传示例,包含进度条更新:
<div>
<input type="file" id="file-upload" />
<div class="progress">
<div id="progress-bar" class="progress-bar" style="width: 0%;"></div>
</div>
<p id="progress-text">0%</p>
</div>
$('#file-upload').on('change', function(e) {
var file = e.target.files[0];
var formData = new FormData();
formData.append('file', file);
var uploadDeferred = $.Deferred();
// 进度回调:更新进度条
uploadDeferred.progress(function(percentage) {
$('#progress-bar').css('width', (percentage * 100) + '%');
$('#progress-text').text(Math.round(percentage * 100) + '%');
});
// 成功回调
uploadDeferred.done(function(response) {
alert('上传成功:' + response.filename);
});
// 失败回调
uploadDeferred.fail(function(xhr, status, error) {
alert('上传失败:' + error);
});
// 使用 jQuery.ajax 发起上传请求
$.ajax({
url: '/upload',
type: 'POST',
data: formData,
contentType: false,
processData: false,
xhr: function() {
var xhr = new window.XMLHttpRequest();
// 监听上传进度
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
var percent = e.loaded / e.total;
uploadDeferred.notify(percent); // 触发 progress 回调
}
}, false);
return xhr;
}
})
.done(function(response) {
uploadDeferred.resolve(response);
})
.fail(function(xhr, status, error) {
uploadDeferred.reject(xhr, status, error);
});
});
这个例子中:
xhr.upload.addEventListener('progress')是原生的进度监听。uploadDeferred.notify(percent)将原生事件转换为 jQuery 的deferred机制。- 所有 UI 更新都通过
progress()回调完成,逻辑清晰。
高级用法:多个 progress 回调与链式调用
deferred.progress() 支持注册多个回调函数,它们会按顺序执行。这在需要多个组件响应进度时非常有用。
var task = $.Deferred();
// 注册多个进度回调
task.progress(function(perc) {
console.log('模块 A:进度更新到 ' + (perc * 100).toFixed(1) + '%');
});
task.progress(function(perc) {
console.log('模块 B:当前进度 ' + (perc * 100).toFixed(1) + '%');
});
// 模拟任务
setTimeout(function() {
task.notify(0.25);
}, 1000);
setTimeout(function() {
task.notify(0.75);
}, 2000);
setTimeout(function() {
task.resolve();
}, 3000);
输出结果:
模块 A:进度更新到 25.0%
模块 B:当前进度 25.0%
模块 A:进度更新到 75.0%
模块 B:当前进度 75.0%
此外,deferred.progress() 可以与 then()、always() 等方法链式使用,形成完整的异步流程:
var deferred = $.Deferred();
deferred.progress(function(perc) {
console.log('当前进度:' + (perc * 100).toFixed(2) + '%');
});
deferred.then(
function(success) {
console.log('任务成功:', success);
},
function(error) {
console.log('任务失败:', error);
}
);
// 触发进度
deferred.notify(0.5);
deferred.resolve('任务完成');
总结
jQuery deferred.progress() 方法是 jQuery 异步编程中一个不可或缺的工具。它让开发者能够实时感知异步任务的执行状态,为用户界面提供流畅的反馈体验。
无论是文件上传、动画播放,还是复杂的数据处理,只要涉及到“进行中”的状态,progress() 都能派上用场。它与 done()、fail() 配合使用,构建出完整、清晰的异步流程。
虽然现代 JavaScript 已经有了 Promise 和 async/await,但 jQuery 的 deferred 机制在特定场景下依然有其价值,尤其是在维护老项目或需要兼容旧版浏览器时。
掌握 deferred.progress() 方法,不仅能让你写出更友好的前端应用,也能加深对异步编程本质的理解。它提醒我们:异步不只是“成功或失败”,还有“正在发生”的中间状态。而这个状态,正是用户体验的分水岭。