Service Worker · API
Periodic Background Sync API
发布于
Periodic Background Sync API 让已安装的 web 应用注册一个带名字的任务,由浏览器按自己选择的时机在 service worker 中运行,频率不高于应用要求的 minInterval,这样用户下次打开应用时内容已经是新的。它自 Chrome 80、Edge 80、Samsung Internet 13 起可用,Firefox 与 Safari 均未实现(BCD api.PeriodicSyncManager),而且 Chrome 只把它授予已安装、且站点参与度分数大于零的应用。
// 页面或 service worker 中const registration = await navigator.serviceWorker.ready;await registration.periodicSync.register(tag, { minInterval });const tags = await registration.periodicSync.getTags();await registration.periodicSync.unregister(tag);
// service worker 中self.addEventListener('periodicsync', (event) => { event.waitUntil(refresh(event.tag));});register() 在注册写入存储后返回 Promise<undefined>;对已存在的 tag 再次注册会替换它的 minInterval。getTags() 兑现为所有仍在注册中的 tag,unregister() 无论 tag 是否存在都兑现为 undefined。periodicsync 事件只携带 tag;与一次性后台同步不同,它没有 lastChance,因为一次失败的运行之后只是等待下一次排期。
该方法接受一个 tag 与一个选项对象。
| 名称 | 类型 | 说明 |
|---|---|---|
tag |
string |
标识这次注册,并以 PeriodicSyncEvent.tag 送达。每个 tag 一条注册;多个 tag 可以按不同节奏运行。 |
options.minInterval |
number |
两次运行之间的最小毫秒数。浏览器把它当作下限而不是时间表:Chrome 让实际频率与应用的使用频率对齐,同时考虑设备的电量与网络状态,参与度分数为零时则完全跳过。 |
Chrome 还要求存在一个此前用过的网络才会触发事件,因此设备连上从未见过的网络时,用户在该网络上打开一次应用之前不会同步。
register() 在三种情况下拒绝(MDN,PeriodicSyncManager.register())。
InvalidStateError(DOMException):注册上没有 active 状态的 service worker。先await navigator.serviceWorker.ready。NotAllowedError(DOMException):periodic-background-sync权限未授予。在 Chrome 中,任何普通浏览器标签页都处于这个状态,因为该权限只授予已安装的应用;同样的代码在应用从自己的窗口启动后就能成功。InvalidAccessError(DOMException):调用方窗口不是顶层或辅助浏览上下文,例如 iframe。TypeError:Firefox 与 Safari 中registration.periodicSync为undefined,访问其成员本身就会抛出。用'periodicSync' in registration做特性检测。
示例共用位于 /sw.js 的一个 service worker,以及名为 news 的新闻缓存。
检查权限后注册每日刷新
Section titled “检查权限后注册每日刷新”页面在调用 register() 之前先查询权限,未安装的应用因此不会撞上 NotAllowedError,而是改为向用户显示“打开时刷新”的提示。
async function enableDailyRefresh() { const status = await navigator.permissions.query({ name: 'periodic-background-sync' }); if (status.state !== 'granted') { showNotice('安装应用后,头条会在后台自动刷新。'); return; } const registration = await navigator.serviceWorker.ready; await registration.periodicSync.register('news-refresh', { minInterval: 24 * 60 * 60 * 1000, });}Firefox 与 Safari 对未知权限名的 permissions.query() 会抛出 TypeError,跨浏览器的调用方应把查询包在 try/catch 中,并把失败视为“未授予”。
在 service worker 中处理事件
Section titled “在 service worker 中处理事件”worker 拉取并存储新闻;抛出的错误会让 waitUntil() 的 Promise 被拒绝,浏览器记录后忽略,直到下一次排期。
self.addEventListener('periodicsync', (event) => { if (event.tag !== 'news-refresh') return; event.waitUntil((async () => { const response = await fetch('/api/headlines', { cache: 'no-store' }); if (!response.ok) throw new Error(`headlines: ${response.status}`); const cache = await caches.open('news'); await cache.put('/api/headlines', response); })());});工作量要短:Chrome 在自己的执行时限到达后结束事件,仍在运行的 worker 会被终止,缓存写入也随之中断。
检测支持并改为返回页面时刷新
Section titled “检测支持并改为返回页面时刷新”periodicSync 缺失或权限未授予时,回退方案在页面重新可见时刷新,对普通标签页或未安装的应用同样达成“打开即新”的目标,代价是一次可见的网络等待。
async function setUpRefresh(refreshNow) { const registration = await navigator.serviceWorker.ready; if ('periodicSync' in registration) { try { await registration.periodicSync.register('news-refresh', { minInterval: 24 * 60 * 60 * 1000 }); return; } catch { // 普通标签页中会被拒绝:落入前台回退。 } } document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') refreshNow(); }); refreshNow();}回退只在页面存在期间运行,无法为已关闭的应用刷新内容,这正是该 API 所填补的空缺。
- Web Periodic Background Synchronization: register() method(wicg.github.io)
- Richer offline experiences with the Periodic Background Sync API(developer.chrome.com)
- PeriodicSyncManager: register() method(developer.mozilla.org)
- Periodic Background Sync 浏览器支持
- Background Sync API
- 用于内容更新的周期性后台同步
- Service worker 生命周期
规范
| 规范 | 状态 |
|---|---|
| 无。 | |