
本文介绍如何通过服务端流式响应(server-send events 或 streaming json)让前端在大量并发请求完成前就逐步接收并渲染结果,避免长时间等待全部响应结束。
本文介绍如何通过服务端流式响应(server-send events 或 streaming json)让前端在大量并发请求完成前就逐步接收并渲染结果,避免长时间等待全部响应结束。
在处理上百个并发 fetch 请求时,传统 Promise.all() 方式要求所有请求完全结束后才统一返回结果,导致前端长时间无响应、用户体验差。真正的优化方向不是“前端加速”,而是重构通信模型:从「批量阻塞式响应」转向「增量流式响应」。
✅ 推荐方案:后端流式输出 + 前端流式消费
1. 后端改造(以 Node.js/Express 为例)
使用 res.write() 分块推送 JSON 片段,并设置正确的响应头:
// Express 路由示例
app.post('/api/batch-search', async (req, res) => {
res.writeHead(200, {
'Content-Type': 'application/json',
'X-Content-Type-Options': 'nosniff',
// 启用流式传输(禁用缓冲)
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
const { originList, destinationList } = req.body;
const results = [];
// 并发执行但立即推送每个完成项
const fetchPromises = [];
for (const origin of originList) {
for (const destiny of destinationList) {
const url = `https://api.example.com/route?from=${origin}&to=${destiny}`;
fetchPromises.push(
fetch(url)
.then(r => r.json())
.then(data => {
const item = { origin, destiny, text: data.text };
results.push(item);
// ? 立即写入一个 JSON 对象(注意:需保证格式可解析)
res.write(JSON.stringify(item) + '\n'); // 每行一个 JSON 对象(NDJSON)
})
.catch(err => {
const errorItem = { origin, destiny, error: err.message };
res.write(JSON.stringify(errorItem) + '\n');
})
);
}
}
// 等待全部发起(非全部完成),但不阻塞输出
await Promise.allSettled(fetchPromises);
res.end(); // 标记流结束
});
⚠️ 注意事项:
- 使用
Promise.allSettled()避免单个失败中断整个流;- 响应体采用 NDJSON(Newline-Delimited JSON) 格式(每行一个合法 JSON),便于前端逐行解析;
- 必须设置
res.flush()或确保服务器未启用响应缓冲(如 Express 默认可能缓存,可用res.flushHeaders()或切换至原生http.ServerResponse更可靠);- 生产环境建议搭配
compression: false或流式压缩库(如compressible+zlib流)。
2. 前端消费(React + ReadableStream)
利用 response.body.getReader() 流式读取并实时更新 UI:
前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。
// React 组件内
const handleStream = async () => {
const response = await fetch('/api/batch-search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ originList, destinationList }),
});
if (!response.body) throw new Error('ReadableStream not supported');
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 });
// 按换行符分割 NDJSON
const lines = buffer.split('\n').filter(line => line.trim() !== '');
buffer = lines.pop() || ''; // 保留不完整行
for (const line of lines) {
try {
const item = JSON.parse(line);
setResults(prev => [...prev, item]); // 实时更新列表
} catch (e) {
console.warn('Invalid JSON line:', line);
}
}
}
};
3. 替代轻量方案(SSE)
若需更简单的心跳与重连支持,可改用 Server-Sent Events(SSE):
- 后端发送
text/event-stream,每条data: {...}\n\n; - 前端用
EventSource自动重连、解析; - 适合仅需单向推送(服务端→前端)且对延迟敏感的场景。
✅ 总结
- ❌ 不要依赖前端“提前拿到 Promise”——瓶颈在通信模型;
- ✅ 用 流式响应(NDJSON/SSE) 解耦后端处理与前端展示;
- ✅ 结合
Promise.allSettled+res.write实现高并发下的渐进式交付; - ✅ 前端配合流式读取 + 增量
setState,实现毫秒级可见反馈。
该模式显著提升长耗时批处理任务的感知性能,是现代 Web 应用处理大规模异步数据的标准实践。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!








