demo · v149
Heartbeat Recovery
Many WebSocket clients send periodic heartbeats (ping/pong or application-level keepalives) to keep the connection alive and detect server-side disconnects. When Chrome 149 closes the WebSocket on BFCache entry, the heartbeat timer must be cancelled on freeze and restarted with a fresh connection on restore — otherwise the restored page sends heartbeats to a dead socket.
Simulate a BFCache freeze/restore cycle and watch the heartbeat lifecycle. A correctly-written handler cancels the heartbeat interval in
pagehide, reconnects in pageshow(persisted:true), and restarts the heartbeat. An incorrect handler keeps the old interval running and detects the dead socket eventually via a missed pong.
Heartbeat interval:
1.5s
WebSocket State
SIMULATED
Heartbeat not started
HB interval: —
Last ping: —
Missed pongs: 0
Last ping: —
Missed pongs: 0
BFCache State
Active
Freeze count: 0
Restore count: 0
Restore count: 0
Handler Mode
Correct: cancels interval on freeze, reconnects + restarts on restore.
0msSimulation ready. Connect to begin.
Heartbeats sent0
Missed pongs0
Reconnects0
HB after dead socket0
// Correct pattern: cancel heartbeat on freeze, restart on restore
let ws = null;
let heartbeatTimer = null;
const HB_INTERVAL = 15000; // 15 seconds
function connect() {
ws = new WebSocket('wss://example.com/ws');
ws.addEventListener('open', () => startHeartbeat());
ws.addEventListener('close', () => stopHeartbeat());
ws.addEventListener('message', (e) => {
if (e.data === 'pong') resetMissedPongs();
});
}
function startHeartbeat() {
stopHeartbeat(); // always clear before starting
heartbeatTimer = setInterval(() => {
if (ws?.readyState === WebSocket.OPEN) {
ws.send('ping');
}
}, HB_INTERVAL);
}
function stopHeartbeat() {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
}
// Chrome 149: WebSocket is closed on BFCache entry
window.addEventListener('pagehide', (e) => {
if (e.persisted) {
stopHeartbeat(); // cancel — socket is closed by Chrome 149
}
});
window.addEventListener('pageshow', (e) => {
if (e.persisted) {
connect(); // reconnect on restore
// startHeartbeat() is called by the 'open' event
}
});
see also
implementation reference
Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗