如何用Express发送对象数组?前后端方案抉择
方案可行性与弊端分析
客户端多请求方案的可行性
这个方案完全可行。服务端提供单集合查询接口,客户端并行发起多个请求对应不同关键词的集合查询,这种模式在实际开发中是常见的实现方式。
存在的弊端
- 请求开销与并发限制问题
你需要发起11个HTTP请求,而浏览器对同一域名的并发请求数有默认限制(通常为6-8个),超出的请求会排队等待,反而拖慢整体数据获取速度。同时每个HTTP请求都包含握手、头部传输等额外开销,大量请求会增加客户端和服务端的资源消耗。 - 客户端逻辑复杂度提升
需要在客户端管理多个请求的状态,处理每个请求的成功/失败情况,还要合并返回数据。如果某一个请求失败,需要额外处理重试或错误提示逻辑,增加前端代码的维护成本。 - 服务端负载放大
原本一次请求就能完成的查询变为11次,服务端需要处理更多的HTTP连接、解析更多请求参数,MongoDB也会收到更多查询请求,在高并发场景下会显著放大服务端的负载压力。 - 跨域额外开销
若前后端不在同一域名下,每个请求都会触发跨域预检(OPTIONS请求),进一步增加请求延迟和资源消耗。
原后端方案的改进思路
你想要的分块返回有效JSON+实时返回是可以实现的,无需改成客户端多请求模式,只需调整后端输出逻辑:
方法1:采用JSON Lines格式流式返回
将每个查询结果作为独立JSON对象,用换行符分隔,客户端可逐行解析:
async function retrieve_multiple(req, res) { const search_string = req.query.search_string; const limit = req.query.limit; const keywords = [ 'are', 'can', 'did', 'does', 'how', 'should', 'what', 'when', 'which', 'who', 'why' ]; // 设置响应头,声明为JSON Lines流 res.set({ 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/jsonl', 'Transfer-Encoding': 'chunked' }); // 逐个查询并实时返回结果 for (const keyword of keywords) { try { const data = await models.getArticle(keyword, search_string, limit); res.write(JSON.stringify(data) + '\n'); } catch (err) { res.write(JSON.stringify({ error: err.message, keyword }) + '\n'); } } res.end(); }
客户端解析逻辑示例:
async function fetchStreamData() { const response = await fetch('/api/retrieve-multiple?search_string=xxx&limit=10'); const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop(); // 保留未完成的最后一行 for (const line of lines) { if (!line) continue; const data = JSON.parse(line); // 处理单个数据块 console.log('接收数据:', data); } } }
方法2:使用Server-Sent Events(SSE)
SSE是专门用于服务端向客户端推送事件流的标准方案,更适合实时场景:
async function retrieve_multiple(req, res) { const search_string = req.query.search_string; const limit = req.query.limit; const keywords = [ 'are', 'can', 'did', 'does', 'how', 'should', 'what', 'when', 'which', 'who', 'why' ]; // 设置SSE响应头 res.set({ 'Access-Control-Allow-Origin': '*', 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // 逐个查询并推送结果 for (const keyword of keywords) { try { const data = await models.getArticle(keyword, search_string, limit); res.write(`data: ${JSON.stringify(data)}\n\n`); await new Promise(resolve => res.flushHeaders(resolve)); // 强制刷新发送数据 } catch (err) { res.write(`data: ${JSON.stringify({ error: err.message, keyword })}\n\n`); } } // 发送结束事件 res.write('event: close\ndata: all done\n\n'); res.end(); }
客户端监听逻辑:
const eventSource = new EventSource('/api/retrieve-multiple?search_string=xxx&limit=10'); eventSource.onmessage = (event) => { const data = JSON.parse(event.data); console.log('接收数据:', data); }; eventSource.addEventListener('close', () => { console.log('所有数据接收完成'); eventSource.close(); }); eventSource.onerror = (err) => { console.error('SSE错误:', err); eventSource.close(); };
内容的提问来源于stack exchange,提问作者Muhammad Yahya Warraich
相关产品推荐
相关产品推荐

