长轮询本质是Ajax变体,非持续连接而是“发一次、等数据、响应即重发”;服务器挂起响应直至有新数据或超时,客户端收到后立即发起下一轮请求,兼顾兼容性与实时性。

长轮询(Long Polling)本质是 Ajax 的一种变体,不是持续连接,而是“发一次请求,等有数据再响应,响应完立刻再发”。它比普通轮询省流量、延迟低,又比 WebSocket 兼容性更好。实现关键在于:服务器不立即返回,而是挂起响应,直到有新数据或超时;客户端收到响应后,不管成功与否,立刻发起下一次请求。
核心思路:请求发出去,不急着收,等服务端“推”
普通 Ajax 请求发完就等响应,而长轮询要求服务端在没有数据时先 hold 住连接(比如 Node.js 用 res.write() 不 end(),或 Express 中用 res.setTimeout() 控制等待上限)。客户端拿到响应后,马上再调用同一函数,形成链式请求流。
前端 JavaScript 实现(原生 fetch + 递归)
用 fetch 发起请求,处理成功/失败/超时,然后自动重连:
function longPoll() {
fetch('/api/long-poll', {
method: 'GET',
headers: { 'Cache-Control': 'no-cache' }
})
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then(data => {
console.log('收到新数据:', data);
// 处理业务逻辑,比如更新 UI
renderUpdate(data);
// 立即发起下一次请求
longPoll();
})
.catch(err => {
console.warn('长轮询中断,2秒后重试:', err.message);
// 出错后延迟重试,避免雪崩
setTimeout(longPoll, 2000);
});
}
// 启动
longPoll();
服务端配合要点(以 Express 为例)
后端不能一来就 res.json(),得等事件发生或超时才响应:
立即学习“Java免费学习笔记(深入)”;
- 用一个数组或 Map 存储待响应的请求(每个请求对应一个 res 对象)
- 当有新数据(如消息到达、状态变更),遍历所有挂起的响应,逐一 res.json(data)
- 每个请求设超时(如 30 秒),超时后主动 res.status(204).end(),让前端继续轮
- 注意清理已响应或断开的连接(监听 req.on('close'))
注意事项和优化建议
- 避免无限递归栈增长:用 setTimeout(longPoll, 0) 替代直接调用,确保异步清栈
- 加唯一标识或时间戳:防止重复数据或乱序,比如每次请求带 lastId=123,服务端只返回更新过的
- 超时时间要合理:太短增加无效请求,太长影响实时性;通常 20–45 秒较稳妥
- 服务端需支持连接保持:Nginx 默认关掉长连接,需配置 proxy_read_timeout 和 keepalive_timeout
长轮询不依赖 WebSocket,老浏览器也能跑,适合中低频实时场景。真正高频、双向交互多的,还是推荐升级到 SSE 或 WebSocket。


















