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

如何将Java Controller中Executor Service的执行结果输出到前端视图

问题原因

你当前的实现逻辑里,submit接口收到请求后,把doStuff扔给线程池异步执行就直接返回了ok,此时对应的HTTP请求生命周期已经完全结束,后续doStuff的执行结果没有办法通过已经关闭的HTTP连接返回给前端,所以你直接在doStuff里返回值也无法传递到前端。


可选解决方案

方案1:前端短轮询(实现成本最低,适合大多数场景)

不需要改造现有架构,只需要加少量代码即可实现:

  • 后端逻辑调整:
    1. 生成唯一的任务ID,异步任务执行完成后,把结果x和任务ID绑定存入缓存(单节点部署用本地Map,分布式部署用Redis)
    2. submit接口不再只返回ok,同时返回生成的任务ID给前端
    3. 新增查询接口,接收任务ID,返回对应任务的执行状态和结果
  • 前端逻辑调整:
    1. 拿到任务ID后,每隔固定时间调用一次查询接口拉取任务状态
    2. 查到任务执行完成后,把结果渲染到页面,停止轮询

改造后的代码示例

后端代码

// 本地任务结果缓存,分布式场景替换为Redis即可
private ConcurrentHashMap<String, String> taskResultCache = new ConcurrentHashMap<>();

@RequestMapping(value = "/submit", method = RequestMethod.POST, produces = "application/json")
public @ResponseBody String submitSurvey1(@ModelAttribute("uploadForm") UploadTest uploadForm, Model map,
                                          HttpSession session, HttpServletRequest request) {
    // 生成唯一任务ID
    String taskId = UUID.randomUUID().toString();
    // 提交异步任务
    executor.submit(() -> {
        String result = doStuff(uploadForm, map, session, request);
        // 执行完成后缓存结果
        taskResultCache.put(taskId, result);
    });
    // 返回任务ID给前端
    return "{\"code\":\"ok\",\"taskId\":\""+taskId+"\"}";
}

// 新增任务结果查询接口
@RequestMapping(value = "/getTaskResult", method = RequestMethod.GET, produces = "application/json")
public @ResponseBody String getTaskResult(@RequestParam String taskId) {
    String result = taskResultCache.get(taskId);
    if(result == null){
        return "{\"status\":\"processing\"}";
    }else {
        // 取完结果删除缓存,避免内存泄漏
        taskResultCache.remove(taskId);
        return "{\"status\":\"finished\",\"result\":"+result+"}";
    }
}

// doStuff修改返回值为String,返回执行结果x
private String doStuff(@ModelAttribute("uploadForm") UploadTest uploadForm, Model map,
                       HttpSession session, HttpServletRequest request){
    String x = "";
    try {
        token.regenToken(session);
        JSONObject hit = new JSONObject();
        hit.put("pathfile", "path");
        hit.put("filename", "filename");
        hit.put("submitted_by", session.getAttribute("session_id").toString());

        JSONObject json = new JSONObject(new RestServiceUnirest().requestPost(
                this.API + "/SubmissionLimit/uploadrecord/ec",
                session.getAttribute("accesskey").toString(), hit.toString()));

        x = json.toString();
    } catch (Exception e) {
        file.delete();
        x = "{\"error\":\""+e.getMessage()+"\"}";
    }
    return x;
}

前端代码

$("#formInput").submit(function (e) {
    $("#submitFile").html("<i class='fa fa-circle-o-notch fa-spin'></i> Load..").prop("disabled", true);
    $.ajax({
      type: "POST",
      url: "${local_server}/UploadController/submit",
      data: new FormData(this),
      processData: false,
      contentType: false,
      cache: false,
      dataType: "json",
      
      success: function (res) {
          if(res.code == "ok"){
              const taskId = res.taskId;
              // 每2秒轮询一次结果
              const pollTimer = setInterval(()=>{
                  $.get("${local_server}/UploadController/getTaskResult", {taskId: taskId}, function(data){
                      if(data.status == "finished"){
                          // 这里替换为你自己的页面渲染逻辑
                          console.log("执行结果:", data.result);
                          // 恢复按钮状态
                          $("#submitFile").html("提交").prop("disabled", false);
                          // 停止轮询
                          clearInterval(pollTimer);
                      }
                  }, "json");
              }, 2000);
              // 额外加超时逻辑,避免任务异常一直轮询
              setTimeout(()=>{
                  clearInterval(pollTimer);
                  $("#submitFile").html("提交").prop("disabled", false);
                  alert("任务执行超时,请稍后重试");
              }, 5*60*1000)
          }
      }
    });
    
    e.preventDefault();
});

方案2:WebSocket主动推送(实时性更高)

如果业务对结果通知的延迟要求很高,可以选择WebSocket方案:

  1. 前端页面加载时,和后端建立WebSocket长连接,绑定当前用户的sessionId
  2. 后端doStuff执行完成后,直接通过对应session的WebSocket连接,把结果主动推送给前端
  3. 前端收到推送消息后直接渲染到页面即可,不需要轮询

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 15:06:01