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

如何在服务器完成数据处理后向前端返回处理后的数据?

解决方案:让API路由等待数据就绪后再返回

你当前的代码存在两个核心问题:全局data变量会被所有请求共享导致数据冲突,且GET请求没有等待数据就绪的逻辑。以下是两种可行的解决思路:

思路1:基于任务ID的内存缓存+轮询等待

给每个文档处理任务分配唯一ID(比如上传文档时生成的UUID),用Map存储不同任务的处理状态,GET请求根据任务ID等待数据就绪。

修改后的代码示例:

// 用Map存储每个任务的处理数据,key为任务ID,value为处理完成的数据
const taskDataMap = new Map();

export default async function handler(req, res) {
  const { method } = req;
  
  if (method === 'POST') {
    try {
      // 从请求体获取任务ID和对应数据
      const { taskId, data } = req.body;
      if (!taskId) {
        return res.status(400).json({ error: '缺少taskId参数' });
      }
      // 存储数据到Map
      taskDataMap.set(taskId, data);
      console.log(`任务${taskId}的数据已存储`);
      return res.status(200).json({ success: true });
    } catch (error) {
      console.error(`接收Workflow数据失败: ${error}`);
      return res.status(400).json({ error: error.message });
    }
  }

  if (method === 'GET') {
    try {
      const { taskId } = req.query;
      if (!taskId) {
        return res.status(400).json({ error: '缺少taskId查询参数' });
      }

      // 设置超时时间(比如30秒),避免请求无限挂起
      const timeoutMs = 30000;
      const startTime = Date.now();
      
      // 轮询等待数据就绪
      while (Date.now() - startTime < timeoutMs) {
        const data = taskDataMap.get(taskId);
        if (data) {
          // 返回数据后删除缓存,避免内存占用
          taskDataMap.delete(taskId);
          return res.status(200).json({ data });
        }
        // 每隔500ms轮询一次
        await new Promise(resolve => setTimeout(resolve, 500));
      }

      // 超时返回错误
      return res.status(408).json({ error: '等待数据超时,请稍后重试' });
    } catch (error) {
      console.error(`返回数据失败: ${error}`);
      return res.status(500).json({ error: error.message });
    }
  }

  // 处理其他请求方法
  res.setHeader('Allow', ['GET', 'POST']);
  return res.status(405).end(`Method ${method} Not Allowed`);
}

思路2:直接监听Firestore文档变化

既然数据最终会存入Firestore,GET请求可以直接监听目标文档的变化,直到数据出现再返回:

import { getFirestore, doc, onSnapshot } from 'firebase-admin/firestore';

// 初始化Firestore(如果还没初始化)
const db = getFirestore();

export default async function handler(req, res) {
  const { method } = req;
  
  if (method === 'POST') {
    // 接收Workflow的数据并存入Firestore
    try {
      const { taskId, data } = req.body;
      if (!taskId) {
        return res.status(400).json({ error: '缺少taskId参数' });
      }
      await db.doc(`processedDocs/${taskId}`).set(data);
      console.log(`任务${taskId}的数据已存入Firestore`);
      return res.status(200).json({ success: true });
    } catch (error) {
      console.error(`接收Workflow数据失败: ${error}`);
      return res.status(400).json({ error: error.message });
    }
  }

  if (method === 'GET') {
    try {
      const { taskId } = req.query;
      if (!taskId) {
        return res.status(400).json({ error: '缺少taskId查询参数' });
      }

      const docRef = db.doc(`processedDocs/${taskId}`);
      let unsubscribe;

      // 设置超时
      const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => {
          unsubscribe?.();
          reject(new Error('等待数据超时'));
        }, 30000);
      });

      // 监听文档变化
      const dataPromise = new Promise((resolve) => {
        unsubscribe = onSnapshot(docRef, (snapshot) => {
          if (snapshot.exists) {
            resolve(snapshot.data());
            unsubscribe();
          }
        });
      });

      // 等待数据或超时
      const data = await Promise.race([dataPromise, timeoutPromise]);
      return res.status(200).json({ data });
    } catch (error) {
      console.error(`返回数据失败: ${error}`);
      return res.status(408).json({ error: error.message });
    }
  }

  res.setHeader('Allow', ['GET', 'POST']);
  return res.status(405).end(`Method ${method} Not Allowed`);
}

注意事项

  • 内存缓存方案(思路1)适合短期存储,重启服务后缓存会清空,若需要持久化优先用Firestore方案。
  • 必须设置合理的超时时间,避免请求长期挂起占用服务器资源。
  • 每个任务必须有唯一ID,确保数据不会被其他请求混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 17:37:32