← 返回题目列表

前端轮询如何设计?固定轮询、自适应轮询和退避有什么区别?

中等 第 16 / 26 题 更新于 2026/07/29
前端网络轮询退避实时更新

简化版

轮询是前端按一定间隔反复请求服务端,用来获取任务状态、消息数量或异步处理结果。设计时要避免固定频率无脑请求,应考虑页面可见性、错误退避、最大次数、用户离开取消、服务端压力和结果终态。固定轮询简单但浪费;自适应轮询会根据状态调整间隔;指数退避适合失败或服务繁忙时降压。

详细版

轮询适合“实时性要求不极致,但需要持续刷新”的场景。

策略特点场景
固定轮询每 N 秒请求简单状态刷新
自适应轮询根据状态调整间隔异步任务进度
指数退避失败后逐渐拉长服务繁忙或弱网
可见性暂停页面后台停止或降频节省资源
let stopped = false

async function poll() {
  while (!stopped) {
    const data = await fetchStatus()
    if (data.done) break
    await sleep(2000)
  }
}

轮询不是 setInterval 写完就结束,取消、退避和终态才是工程重点。

完整版教学

一、轮询适合什么场景

轮询适合异步任务状态、导出进度、支付结果查询、通知数量、审核状态等场景。

它的优点是实现简单、兼容性好、服务端要求低。缺点是有延迟、浪费请求、服务端压力较大。

二、不要默认 setInterval

setInterval 容易出现请求堆叠:上一次请求还没回来,下一次又发出。

更好的方式是“请求完成后再等待下一轮”。

async function loop() {
  while (running) {
    await request()
    await sleep(interval)
  }
}

这样不会因为接口慢而堆积。

三、终态和最大次数

轮询必须知道什么时候停止。终态可能是成功、失败、取消、超时或超过最大次数。

终止条件说明
status = done任务完成
status = failed任务失败
maxAttempts避免无限轮询
页面离开取消轮询
用户主动取消停止请求

没有停止条件的轮询就是隐形性能问题。

四、错误退避

接口失败时不要继续高频请求。可以把间隔按指数增长。

const delay = Math.min(base * 2 ** failCount, maxDelay)

也可以加随机抖动,避免大量客户端同一时间重试。

五、页面可见性

用户切到后台时,轮询可以暂停或降频。

document.addEventListener('visibilitychange', () => {
  if (document.hidden) pausePolling()
  else resumePolling()
})

这样能节省用户电量和服务端资源。

六、和 WebSocket/SSE 的取舍

如果实时性要求高、服务端主动推送多,WebSocket 或 SSE 更合适。如果只是查任务状态,轮询更简单。

方案优点缺点
轮询简单可靠有延迟和浪费
SSE服务端单向推送连接管理
WebSocket双向实时架构复杂

七、常见误区与追问

  • 误区:轮询就是 setInterval。 setInterval 容易请求堆叠,工程上常用完成后再调度下一轮。
  • 误区:接口失败也按原频率请求。 失败时应退避,避免雪上加霜。
  • 误区:用户离开页面轮询也没关系。 后台轮询浪费电量和服务端资源,应暂停或取消。
  • 追问:如何避免请求堆叠? 等本轮请求完成并等待间隔后再发下一轮。
  • 追问:轮询多久停? 由终态、最大次数、总超时时间和用户行为决定。
  • 追问:什么时候不用轮询? 高频实时互动、双向通信更适合 WebSocket,单向实时消息可考虑 SSE。

八、加强记忆

轮询记成“问一次、等一下、再问”。重点是不要堆叠、要有终态、失败退避、后台降频,别让简单方案变成流量黑洞。