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

Express后端定期向Vue2.7前端推送长操作状态更新方案咨询

实现Express后端向Vue前端推送长操作进度的方案

针对你的需求,这里提供两种轻量级、无需WebSocket的实现方案,适配你使用的Express后端和Vue2.7+Axios前端技术栈:


方案1:分块传输编码(Chunked Transfer Encoding)

利用HTTP的分块传输特性,后端逐步发送进度数据,前端通过监听进度事件接收更新,实时性好且无需额外接口。

后端(Express)实现

首先修正原代码的异步逻辑问题,用async/await实现真实的分步执行,通过res.write()发送进度块:

import express from 'express';

const app = express();

app.delete('/long-operation', async (req, res) => {
  // 配置分块传输响应头
  res.setHeader('Transfer-Encoding', 'chunked');
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
  res.setHeader('Connection', 'keep-alive');

  const totalItems = 30000; // 模拟总操作数量
  const updateStep = 100; // 每执行100步发送一次进度

  for (let i = 0; i < totalItems; i++) {
    // 替换为你的实际业务逻辑(这里用1ms延迟模拟)
    await new Promise(resolve => setTimeout(resolve, 1));

    if (i % updateStep === 0) {
      const percentage = Math.round((i / totalItems) * 100);
      const progress = {
        percentage,
        message: `${i} / ${totalItems} deleted`
      };
      // 发送进度块,末尾加换行符方便前端解析
      res.write(JSON.stringify(progress) + '\n');
    }
  }

  // 发送最终完成状态
  const finalProgress = {
    percentage: 100,
    message: `${totalItems} / ${totalItems} deleted`
  };
  res.write(JSON.stringify(finalProgress) + '\n');
  
  res.end();
});

app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

前端(Vue2.7 + Axios)实现

通过Axios的onDownloadProgress监听分块数据,解析并展示最新进度:

<template>
  <div>
    <p>当前进度:{{ progress.percentage }}%</p>
    <p>{{ progress.message }}</p>
    <button @click="startOperation" :disabled="isProcessing">开始删除</button>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      progress: {
        percentage: 0,
        message: '未开始'
      },
      isProcessing: false
    };
  },
  methods: {
    async startOperation() {
      this.isProcessing = true;
      this.progress = { percentage: 0, message: '正在执行...' };
      try {
        await axios.delete('http://localhost:3000/long-operation', {
          onDownloadProgress: (e) => {
            const xhr = e.target;
            // 按换行分割已接收的进度块,过滤空行
            const progressBlocks = xhr.responseText.split('\n').filter(block => block.trim());
            if (progressBlocks.length > 0) {
              // 解析最新的完整进度块
              const latestProgress = JSON.parse(progressBlocks.at(-1));
              this.progress = latestProgress;
            }
          },
          withCredentials: true // 跨域场景下启用
        });
      } catch (err) {
        console.error('操作失败:', err);
        this.progress.message = '操作失败';
      } finally {
        this.isProcessing = false;
      }
    }
  }
};
</script>

方案2:轮询(Polling)

实现简单、兼容性强的方案,前端定期发送GET请求查询进度,后端维护任务状态。

后端(Express)实现

用内存Map存储任务进度,生成唯一任务ID供前端查询:

import express from 'express';
import { v4 as uuidv4 } from 'uuid'; // 需安装:npm install uuid

const app = express();
app.use(express.json());

// 存储任务进度,key为任务ID
const taskMap = new Map();

// 启动长操作接口
app.delete('/long-operation', async (req, res) => {
  const taskId = uuidv4();
  const totalItems = 30000;
  // 初始化进度
  taskMap.set(taskId, {
    percentage: 0,
    message: '0 / 30000 deleted',
    completed: false
  });

  // 异步执行长操作
  (async () => {
    const updateStep = 100;
    for (let i = 0; i < totalItems; i++) {
      await new Promise(resolve => setTimeout(resolve, 1)); // 模拟业务逻辑

      if (i % updateStep === 0) {
        const percentage = Math.round((i / totalItems) * 100);
        taskMap.set(taskId, {
          percentage,
          message: `${i} / ${totalItems} deleted`,
          completed: false
        });
      }
    }
    // 更新完成状态,1分钟后清理过期任务
    taskMap.set(taskId, {
      percentage: 100,
      message: `${totalItems} / ${totalItems} deleted`,
      completed: true
    });
    setTimeout(() => taskMap.delete(taskId), 60000);
  })();

  // 返回任务ID给前端
  res.json({ taskId });
});

// 查询进度接口
app.get('/progress/:taskId', (req, res) => {
  const { taskId } = req.params;
  const progress = taskMap.get(taskId);
  if (!progress) {
    return res.status(404).json({ error: '任务不存在或已完成' });
  }
  res.json(progress);
});

app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

前端(Vue2.7 + Axios)实现

发送DELETE请求获取任务ID,然后定期查询进度:

<template>
  <div>
    <p>当前进度:{{ progress.percentage }}%</p>
    <p>{{ progress.message }}</p>
    <button @click="startOperation" :disabled="isProcessing">开始删除</button>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      progress: {
        percentage: 0,
        message: '未开始'
      },
      isProcessing: false,
      pollTimer: null
    };
  },
  methods: {
    async startOperation() {
      this.isProcessing = true;
      this.progress = { percentage: 0, message: '正在启动...' };
      try {
        // 启动长操作,获取任务ID
        const { data } = await axios.delete('http://localhost:3000/long-operation');
        const { taskId } = data;

        // 每隔500ms查询一次进度
        this.pollTimer = setInterval(async () => {
          try {
            const { data: progressData } = await axios.get(`http://localhost:3000/progress/${taskId}`);
            this.progress = progressData;
            // 任务完成后停止轮询
            if (progressData.completed) {
              clearInterval(this.pollTimer);
              this.isProcessing = false;
            }
          } catch (err) {
            console.error('查询进度失败:', err);
            clearInterval(this.pollTimer);
            this.isProcessing = false;
            this.progress.message = '查询进度失败';
          }
        }, 500);
      } catch (err) {
        console.error('启动操作失败:', err);
        this.isProcessing = false;
        this.progress.message = '操作启动失败';
      }
    }
  },
  beforeUnmount() {
    // 组件销毁时清理定时器
    if (this.pollTimer) clearInterval(this.pollTimer);
  }
};
</script>

方案对比

  • 分块传输:实时性高,无额外请求开销,但需要处理数据流解析,对后端异步逻辑要求严格。
  • 轮询:实现简单,兼容性强,实时性取决于轮询间隔,会产生少量额外HTTP请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 01:00:16