长轮询是什么?它和普通轮询、SSE、WebSocket 有什么区别?
简化版
长轮询是客户端发起请求后,服务端如果暂时没有新数据就先挂起连接,等有数据或超时再返回;客户端收到响应后立刻发起下一次请求。它比普通轮询更实时、更少空响应,但仍基于 HTTP 请求响应模型。相比 SSE,长轮询每次消息都要重新建请求;相比 WebSocket,它不是全双工,适合兼容性要求高、消息频率不太高的场景。
详细版
长轮询常用于旧浏览器兼容、简单通知和消息状态更新。
| 方案 | 连接方式 | 方向 | 特点 |
|---|---|---|---|
| 普通轮询 | 定时短请求 | 客户端拉 | 简单但延迟和浪费高 |
| 长轮询 | 请求挂起等待 | 客户端拉 | 较实时,兼容好 |
| SSE | 长连接 | 服务端推 | 单向推送 |
| WebSocket | 长连接 | 双向 | 实时双向通信 |
async function longPoll() {
while (true) {
const res = await fetch('/api/messages/wait')
const data = await res.json()
handle(data)
}
}
长轮询是“服务端先别急着回答”,不是 WebSocket 的替代品。
完整版教学
一、普通轮询的问题
普通轮询每隔几秒请求一次。如果没有新消息,服务端也要返回空结果。间隔短会浪费资源,间隔长又不够实时。
长轮询试图解决这个折中问题。
二、长轮询的工作流程
流程是:
- 客户端发请求。
- 服务端没有数据就暂时不返回。
- 有数据或达到超时时间后返回。
- 客户端收到后马上发起下一次请求。
这样只要有消息,服务端就能较快返回。
三、前端实现注意点
长轮询要处理错误、超时、取消和重连间隔。
let stopped = false
async function startLongPolling() {
while (!stopped) {
try {
const res = await fetch('/api/wait', { signal })
handle(await res.json())
} catch (e) {
await sleep(1000)
}
}
}
失败后不要立刻疯狂重连。
四、服务端压力
长轮询会让服务端保持大量挂起连接。服务端需要异步 IO、连接超时、队列管理和限流。
如果并发用户很多,长轮询对服务端连接资源有要求。
五、和 SSE 的区别
SSE 是服务端通过一个 HTTP 长连接持续推送文本事件。浏览器提供 EventSource 自动重连。
长轮询每次返回后要重新请求;SSE 则在同一个连接上持续发送。
六、和 WebSocket 的区别
WebSocket 是双向通信,适合聊天室、协作编辑、实时游戏等。长轮询仍是 HTTP 请求响应,只是响应延迟返回。
| 场景 | 更适合 |
|---|---|
| 偶尔有通知 | 长轮询或 SSE |
| 服务端单向推送 | SSE |
| 双向高频互动 | WebSocket |
| 老环境兼容 | 长轮询 |
七、常见误区与追问
- 误区:长轮询就是把普通轮询间隔调长。 长轮询是服务端挂起请求等待数据,不是客户端少问几次。
- 误区:长轮询没有服务端成本。 大量挂起连接会占资源,需要异步模型和限流。
- 误区:长轮询能完全替代 WebSocket。 高频双向通信仍更适合 WebSocket。
- 追问:长轮询失败后怎么办? 做退避重连,并在页面关闭或切路由时取消。
- 追问:为什么要设置服务端超时? 避免连接无限挂起,也让客户端能维持循环。
- 追问:长轮询适合移动端吗? 可用但要考虑电量、网络切换和后台限制。
八、加强记忆
长轮询记成“客户端问,服务端等有消息再答”。它比普通轮询少空转,比 SSE/WebSocket 更兼容,但实时性和连接成本要权衡。