JavaScript无法实现真正即时通讯在线心跳,因HTTP是无状态短连接协议;可通过长轮询或定时短轮询模拟,如fetch+setInterval定时发GET请求检测连接活性,配合AbortController防泄漏,并需服务端同步更新lastHeartbeatAt时间戳。

JavaScript 中无法仅靠 HTTP 请求实现真正意义上的“即时通讯在线心跳”,因为 HTTP 是无状态、短连接协议,不适合持续双向通信。但可以通过“长轮询(Long Polling)”或“定时短轮询(Polling)”模拟心跳机制,用于检测客户端是否在线、维持服务端会话、触发重连等场景。关键不在于“实时推送”,而在于“可控的连接活性探测”。
用 fetch + setInterval 实现基础心跳检测
这是最简单、兼容性最好的方式:前端定时向服务端发送一个轻量 GET 请求(如 /api/heartbeat),服务端返回当前状态(如 { online: true }),前端根据响应判断连接是否正常。
- 建议间隔 20–30 秒,避免过于频繁增加服务端压力
- 请求需携带身份标识(如 token 或 userId),服务端据此更新用户最后活跃时间
- 失败时(网络错误、超时、非 2xx 响应)应记录并尝试恢复,比如暂停后指数退避重试
- 页面隐藏(
document.hidden === true)时可暂停心跳,切回前台再重启
用 AbortController 控制心跳生命周期
防止页面卸载或组件销毁后心跳还在执行,造成内存泄漏或无效请求:
- 每次发起 fetch 前创建新的
AbortController - 在
beforeunload或 React 的useEffect cleanup中调用controller.abort() - fetch 中传入
{ signal: controller.signal },中止后 Promise 会 reject 并抛出AbortError
服务端配合要点(以 Express 为例)
心跳不是单靠前端就能生效的,服务端必须同步维护状态:
立即学习“Java免费学习笔记(深入)”;
- 收到心跳请求后,更新该用户的
lastHeartbeatAt时间戳(存 Redis 最佳) - 提供一个
/api/status接口供管理后台查“最近 60 秒内有心跳的用户” - 可搭配定时任务(如每分钟一次),清理
lastHeartbeatAt超过 90 秒的用户,标记为离线 - 避免在心跳接口中做耗时操作(如数据库写入全字段),只做最小必要更新
为什么不推荐纯 HTTP 心跳做“消息推送”?
HTTP 心跳本身不传递业务消息,它只是“我还在”。若想实现类似微信“已送达”“对方正在输入”的能力,需额外设计:
- 消息发送成功后,由服务端异步触发一次“送达回执”通知(仍需 WebSocket 或 Server-Sent Events)
- “正在输入”状态建议用独立轻量接口(如
POST /api/typing)+ 客户端节流(防抖 1.5s),服务端缓存 5 秒 - 真需要双向实时性,请直接上 WebSocket;HTTP 心跳只是它的补充或降级方案


















