Background sync:重连后重试失败的请求
发布于 更新于
一句话: Background sync(后台同步)让 web 应用的 service worker 推迟某个动作——例如发送一条 排队中的消息——直到浏览器判断设备重新获得网络连接,而不是在网络不可用时直接让它失败。
当某次 fetch 因设备离线而失败时,页面可以注册一个同步请求,而不是直接放弃。浏览器会保留这个注册,
一旦它判断设备已恢复连接,就会在 service worker 中触发 sync 事件,让应用重试这项被推迟的工作——
即便最初注册它的页面早已关闭。
注册与事件 API
Section titled “注册与事件 API”该 API 通过 ServiceWorkerRegistration.sync 访问,它返回一个 SyncManager:
async function queueMessage() { try { await sendMessage(); } catch { const registration = await navigator.serviceWorker.ready; if ('sync' in registration) { await registration.sync.register('send-queued-message'); } }}SyncManager.register(tag)使用给定的 tag 注册一个一次性同步请求,返回的 Promise 在注册 完成后 resolve。SyncManager.getTags()resolve 为当前已注册的 tag 列表。
在 service worker 中,应用监听 sync 事件,并把重试工作的 Promise 传给 waitUntil(),
将事件的生命周期延长。用户代理仍可对这段延长的时间施加自己的执行与生命周期限制,因此
waitUntil() 并不保证工作一定完成——参见下方“常见问题”:
self.addEventListener('sync', (event) => { if (event.tag === 'send-queued-message') { event.waitUntil(sendQueuedMessage()); }});跨浏览器的可用性
Section titled “跨浏览器的可用性”Background sync 是 Chromium(Blink)的特性——Chrome、Edge 和三星 Internet 都已实现,而 WebKit (Safari)和 Gecko(Firefox)尚未实现。
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop) | 支持 | 49 | 高 | 来源 | — |
| Chrome (Android) | 支持 | 49 | 高 | 来源 | 1 |
| Edge (Desktop) | 支持 | 79 | 高 | 来源 | 2 |
| Firefox (Desktop) | 不支持 | — | 高 | 来源 | 3 |
| Firefox (Android) | 不支持 | — | 高 | 来源 | 45 |
| Safari (macOS) | 不支持 | — | 高 | 来源 | 6 |
| Safari (iOS) | 不支持 | — | 高 | 来源 | 78 |
| Samsung Internet | 支持 | 5.0 | 高 | 来源 | 9 |
| WebView (Android) | 不支持 | — | 高 | 来源 | 10 |
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- browser-compat-data 未记录 Firefox 的支持。
- browser-compat-data 未记录 Firefox for Android 的支持。
- 由 browser-compat-data 镜像自 Firefox 的数据推导。
- 实现跟踪:https://webkit.org/b/182565。
- 实现跟踪:https://webkit.org/b/182565。
- 由 browser-compat-data 镜像自 Safari 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
- 实现跟踪:https://crbug.com/40449796。
更多细节见兼容性页面。
- 根据规范,当注册它的页面或 worker 仍在运行时,事件会在连接恢复可用后触发——但不一定是立即 触发。如果该上下文已不再运行,用户代理则只被期望在其“最方便的时机”触发该事件,而不是按任何 有保证的时间表。
- 规范允许用户代理对
SyncEvent施加自己的执行时长与生命周期限制——调用event.waitUntil()并不 保证浏览器会让任意长的工作在终止 worker 之前完成。 - 截至目前,Background sync 在 WebKit(Safari)和 Gecko(Firefox)中不受支持——始终把这种重试 设计为一种增强,而不是成功的唯一路径。
- 根据规范,失败的事件可能会依据用户代理自定的启发式规则重试,除非它是最后一次机会——此时不再 重试。重试并不保证最终一定成功,这也不是一个周期性的计划任务。
如何在运行时检测
Section titled “如何在运行时检测”在注册 tag 之前先检测 SyncManager,并在它不可用时回退为立即重试(或在下次加载页面时重试):
function supportsBackgroundSync(registration) { return 'sync' in registration;}
async function deferOrRetry(registration, tag, retryNow) { if (!supportsBackgroundSync(registration)) { return retryNow(); // 没有 Background Sync:改为立即重试 } return registration.sync.register(tag);}- Background sync:浏览器支持情况 — 浏览器支持矩阵
- Periodic background sync — 一个相关但独立的 API