如何在服务器完成数据处理后向前端返回处理后的数据?
解决方案:让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
相关产品推荐
相关产品推荐

