You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery AJAX批量更新数据时,如何实时展示记录更新进度?

实现长耗时AJAX批量更新的实时进度展示

我来给你梳理下实现这个需求的可行方案,毕竟这种15-20分钟的长耗时批量更新,直接用普通AJAX等待返回肯定行不通,得换个异步轮询的思路,结合你已经在用的日志表来做进度反馈:

核心思路

因为后端批量更新是分批次(每次5000条)执行的,而且已经会把每批次完成后的总记录数写入日志表,那我们可以:

  • 后端把批量更新放到异步后台任务中执行,避免HTTP请求超时断开
  • 前端点击按钮后,先触发启动更新的请求,拿到一个唯一的任务标识
  • 前端通过定时轮询的方式,每隔几秒去后端接口查询该任务的最新进度(从日志表取数据),然后实时展示给用户

前端实现(jQuery)

首先页面上需要按钮和进度展示区域,再配合jQuery写逻辑:

HTML结构

<button id="startUpdateBtn">开始批量更新</button>
<div id="progressDisplay" style="margin-top:10px;">已更新记录数:0</div>

jQuery逻辑

let progressPollTimer; // 存储轮询定时器,方便后续清除

$("#startUpdateBtn").click(function() {
    const $btn = $(this);
    // 禁用按钮防止重复点击
    $btn.prop("disabled", true).text("更新中...");
    $("#progressDisplay").text("已更新记录数:0");

    // 第一步:发起启动更新的请求(后端会启动异步任务)
    $.ajax({
        url: "/start-bulk-update", // 替换成你的后端接口地址
        type: "POST",
        dataType: "json",
        success: function(res) {
            const taskId = res.taskId; // 后端返回的唯一任务标识

            // 第二步:启动轮询,每隔5秒查一次进度
            progressPollTimer = setInterval(() => {
                $.ajax({
                    url: "/get-update-progress", // 进度查询接口
                    type: "GET",
                    data: { taskId: taskId },
                    dataType: "json",
                    success: function(progressData) {
                        // 更新页面展示的进度
                        $("#progressDisplay").text(`已更新记录数:${progressData.updatedCount}`);

                        // 判断更新是否完成
                        if (progressData.isFinished) {
                            clearInterval(progressPollTimer);
                            $btn.prop("disabled", false).text("开始批量更新");
                            $("#progressDisplay").append("<br>✅ 更新已完成!");
                        }

                        // 如果有错误,提示用户
                        if (progressData.errorMsg) {
                            clearInterval(progressPollTimer);
                            $btn.prop("disabled", false).text("开始批量更新");
                            $("#progressDisplay").text(`❌ 更新失败:${progressData.errorMsg}`);
                        }
                    },
                    error: function() {
                        clearInterval(progressPollTimer);
                        $btn.prop("disabled", false).text("开始批量更新");
                        $("#progressDisplay").text("❌ 获取进度失败,请重试");
                    }
                });
            }, 5000); // 轮询间隔,可根据需求调整为3-10秒
        },
        error: function() {
            $btn.prop("disabled", false).text("开始批量更新");
            $("#progressDisplay").text("❌ 启动更新失败,请重试");
        }
    });
});

后端调整要点

后端需要新增两个接口,同时把原有的批量更新逻辑改成异步执行:

1. 启动更新接口

这个接口的作用是启动异步批量更新任务,返回唯一的taskId给前端,不用等待更新完成:

// 示例:Java Spring Boot代码,其他语言逻辑类似
@PostMapping("/start-bulk-update")
public ResponseEntity<Map<String, Object>> startBulkUpdate() {
    // 生成唯一任务ID,用来区分不同用户的更新任务
    String taskId = UUID.randomUUID().toString();
    // 交给异步线程执行批量更新
    bulkUpdateService.executeBulkUpdate(taskId);
    // 返回任务ID给前端
    Map<String, Object> response = new HashMap<>();
    response.put("taskId", taskId);
    return ResponseEntity.ok(response);
}

2. 异步批量更新逻辑

把你原来的bulk collect批量更新逻辑放到异步方法里,每完成5000条就更新日志表,最后标记任务完成:

@Service
public class BulkUpdateService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 异步执行批量更新
    @Async
    public void executeBulkUpdate(String taskId) {
        int totalUpdated = 0;
        boolean isSuccess = true;
        String errorMsg = null;

        try {
            // 循环执行批量更新,直到没有可更新的记录
            while (true) {
                // 执行批量更新SQL,返回本次更新的条数
                int batchCount = jdbcTemplate.update(
                    "UPDATE your_target_table SET ... WHERE ... LIMIT 5000"
                );
                if (batchCount == 0) break; // 没有更多记录,退出循环

                totalUpdated += batchCount;
                // 更新日志表的进度(用ON DUPLICATE KEY来覆盖最新进度)
                jdbcTemplate.update(
                    "INSERT INTO update_log (task_id, updated_count, update_time) " +
                    "VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE updated_count = ?, update_time = NOW()",
                    taskId, totalUpdated, totalUpdated
                );
            }
        } catch (Exception e) {
            isSuccess = false;
            errorMsg = e.getMessage();
            // 记录错误信息到日志表
            jdbcTemplate.update(
                "INSERT INTO update_log (task_id, updated_count, error_msg, update_time) " +
                "VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE error_msg = ?, update_time = NOW()",
                taskId, totalUpdated, errorMsg, errorMsg
            );
        } finally {
            // 标记任务完成状态
            jdbcTemplate.update(
                "INSERT INTO update_log (task_id, updated_count, is_finished, update_time) " +
                "VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE is_finished = ?, update_time = NOW()",
                taskId, totalUpdated, isSuccess, isSuccess
            );
        }
    }
}

3. 进度查询接口

前端轮询时调用这个接口,根据taskId从日志表获取最新的进度和状态:

@GetMapping("/get-update-progress")
public ResponseEntity<Map<String, Object>> getUpdateProgress(@RequestParam String taskId) {
    // 查询最新的进度记录(按时间倒序取第一条)
    Map<String, Object> progress = jdbcTemplate.queryForMap(
        "SELECT updated_count, is_finished, error_msg " +
        "FROM update_log WHERE task_id = ? ORDER BY update_time DESC LIMIT 1",
        taskId
    );
    return ResponseEntity.ok(progress);
}

关键注意事项

  • 任务ID必须唯一:如果有多个用户同时操作,一定要用taskId区分不同的更新任务,避免进度展示混乱
  • 异步任务稳定性:后端要确保异步任务不会被服务器杀死(比如Tomcat要调整线程池配置,或者用专门的任务队列如RabbitMQ、Redis Queue)
  • 轮询间隔合理:不要太频繁(比如1秒一次),建议3-10秒一次,减少服务器压力
  • 异常处理要完善:前端要处理网络断开、后端错误的情况,及时停止轮询并恢复按钮状态;后端要把异常信息写入日志表,方便排查问题

内容的提问来源于stack exchange,提问作者user1463065

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:27:09