工作原理
客户端携带游标或时间戳请求事件。如果没有新数据,服务器不会立即响应,而是挂起请求,直到事件到达或超时。响应返回后(有数据或空超时结果),客户端用最新游标重新发起请求。
let cursor = 0;
while (running) {
const res = await fetch(`/events?since=${cursor}`, { signal });
for (const event of await res.json()) {
cursor = Math.max(cursor, event.id);
handle(event);
}
}
循环看似轮询,但每次迭代都在服务端等待,因此比固定间隔轮询减少了空往返,同时仍停留在普通 HTTP 内:没有升级握手,也不需要特殊的代理支持。
何时使用与取舍
当旧代理、企业防火墙或受限运行时阻断 WebSocket 和 SSE,或更新频率低到不值得维持长连接时,长轮询是务实的选择。它也适用于只提供请求/响应 API 的环境。
每个等待中的客户端都会占用一条连接,往往还占用工作线程;客户端很多时,成本接近每客户端一条长连接,却没有真正流的效率。超时会造成重连突刺,因此需要抖动和退避。若游标不持久,响应与下一次请求之间发生的事件可能丢失。
常见误解
典型错误是把长轮询当成永久订阅。它是有意延迟的重复请求/响应循环,因此遗漏事件处理和游标设计决定正确性。相关概念:Server-Sent Events、超时预算、背压。