不能。background sync 仅在网络恢复且 service worker 活跃时触发,断网时不自动重试;需自行实现请求队列+indexeddb持久化,并在sync事件中重建并发送请求。

Background Sync 能不能在断网时自动重试提交?
不能。Background Sync 本身不感知网络状态,它只在浏览器判断“网络已恢复”后才触发同步事件——也就是说,断网期间注册的 sync 事件会被挂起,直到下次在线且 Service Worker 处于活跃状态时才执行。如果你期望“断网时立刻缓存请求、联网后自动发出去”,必须自己实现请求队列 + 持久化存储(比如 IndexedDB),再在 sync 事件里读取并逐条重试。
如何注册 sync 并确保任务不丢失?
注册前必须确认 Service Worker 已激活,且页面已获得用户权限(Chrome 要求页面有“用户交互”后才能注册 sync)。常见错误是调用 registration.sync.register('submit-form') 后没处理失败情况——比如在非 HTTPS 环境、隐身模式、或未激活的 SW 中会直接抛出 "InvalidStateError" 或 "NotSupportedError"。
- 注册必须放在
sw.js的fetch事件之外,通常在install或message事件中完成 - 务必用
try/catch包裹注册逻辑,并监听registration.sync.getTags()验证是否成功写入 - 不要重复注册同名 tag:多次调用
register('submit-form')不会覆盖,但可能触发多次同步,需在 SW 端做幂等控制
为什么 fetch 事件里拦截表单提交后,sync 里发不出请求?
最常见原因是 Request 对象无法跨事件序列化——你在 fetch 事件中拿到的 request 是一个流式对象,不能直接存进 IndexedDB 或传给 sync 事件。必须在拦截时手动提取关键字段(url、method、headers、body),序列化为纯 JS 对象再持久化。
-
request.clone().json()或.text()可读取 body,但注意 body 只能读一次,读完原 request 就失效 - 避免存
request.headers原对象,改用Object.fromEntries(request.headers.entries()) - 同步事件里重建请求要用
new Request(url, { method, headers, body }),不是复用旧 request
sync 事件里重试失败了怎么办?
Background Sync 没有重试机制:一次 sync 事件要么成功完成,要么失败后该 tag 就被丢弃,不会再自动触发。你必须在 sync 事件 handler 中显式捕获异常,并在失败时重新注册同名 tag(前提是还有未完成任务)。
- 每次 sync 执行前先查 IndexedDB 是否还有待发请求;没有就直接 return
- 每发送一个请求都用
await fetch(),失败则记录错误、保留该请求、break 循环 - 最后检查队列长度,如果仍有剩余,调用
self.registration.sync.register('submit-form')续约 - 注意节流:不要在失败后立即重注册,建议加个简单退避(如
setTimeout(() => register(), 5000))
真正麻烦的不是注册 sync,而是把请求从 fetch 事件安全地搬运到 sync 事件里——body 序列化、二进制兼容性、并发冲突、失败回滚,这些都得自己兜底。Chrome 的 Background Sync 目前仍不支持自定义重试策略或延迟时间,别指望靠它实现“2 小时后重试”。










