1. 引言
本节为非规范性内容。
Web 应用程序经常运行在网络不可靠(例如移动电话)且生命周期未知 的环境中(浏览器可能被终止,或者用户可能导航离开)。这使得 将来自 Web 应用的客户端数据(例如照片上传、文档更改或撰写的电子邮件) 与服务器同步变得困难。如果浏览器在同步完成前关闭,或用户导航离开, 应用就必须等到用户重新访问页面时再尝试。本规范提供了一个新的 onsync service worker 事件,该事件可 在后台触发, 因而即使在最初请求时处于不利条件下,同步尝试也可以继续。此 API 旨在 缩短内容创建与内容同步到服务器之间的时间。
由于此 API 依赖 service worker,因此此 API 提供的功能仅在 安全上下文中可用。
function sendChatMessage( message) { return addChatMessageToOutbox( message). then(() => { // 等待有作用域的 service worker 注册获得一个 // 处于 active 状态的 service worker return navigator. serviceWorker. ready; }). then( reg=> { return reg. sync. register( 'send-chats' ); }). then(() => { console. log( 'Sync registered!' ); }). catch (() => { console. log( 'Sync registration failed :(' ); }); }
在上面的示例中,addChatMessageToOutbox 是开发者定义的函数。
在 service worker 内响应 sync 事件:
self. addEventListener( 'sync' , event=> { if ( event. tag== 'send-chats' ) { event. waitUntil( getMessagesFromOutbox(). then( messages=> { // 将消息发布到服务器 return fetch( '/send' , { method: 'POST' , body: JSON. stringify( messages), headers: { 'Content-Type' : 'application/json' } }). then(() => { // 成功!将其从发件箱中移除 return removeMessagesFromOutbox( messages); }); }). then(() => { // 将成功情况告知页面,以便页面更新 UI return clients. matchAll({ includeUncontrolled: true }); }). then( clients=> { clients. forEach( client=> client. postMessage( 'outbox-processed' )) }) ); } });
在上面的示例中,getMessagesFromOutbox 和 removeMessagesFromOutbox 是
开发者定义的函数。
2. 概念
如果对于相应 service worker 注册的源,不存在其 service worker clients 的 frame type 为 top-level 或 auxiliary, 则认为 sync 事件在后台运行。
如果用户代理已经建立网络连接,则认为该用户代理 在线。用户代理可以使用更严格的 在线定义。这样的更严格定义可以考虑 service worker 或某个 sync registration 所关联的特定源。
3. 构造
一个 service worker registration 具有关联的 sync registrations 列表,其元素类型是 sync registration。sync registration 是一个由 tag 和 state 组成的元组。
sync registration 具有关联的 tag,即一个 DOMString。
sync registration 具有关联的 registration state,其值为 pending、waiting、firing 或 reregisteredWhileFiring 之一。它 初始设置为 pending。
sync registration 具有关联的 service worker registration。它初始 设置为 null。
在一个 sync registrations 列表中,每个 sync registration 必须具有唯一的 tag。
4. 权限集成
Web Background Synchronization API 是一个 默认强大特性,由 名称 "background-sync" 标识。5. 隐私考虑
5.1. 权限
用户代理可以为用户提供一种禁用后台同步的方式。注: 后台同步应默认启用。权限被拒绝被视为一种例外情况。
5.2. 位置跟踪
当处于后台时,onsync 事件内的 Fetch 请求可能会在 用户离开页面后向服务器透露客户端的 IP 地址。用户代理应通过限制重试次数和 sync 事件的持续时间 来限制跟踪。5.3. 历史泄露
当处于后台时,onsync 事件内的 Fetch 请求可能会向被动窃听者 透露有关客户端导航历史的信息。例如,客户端可能访问站点 https://example.com,该站点注册一个 sync 事件,但直到用户已经导航离开页面并更换网络后才触发。 新网络上的被动窃听者可能会看到 onsync 事件发出的 fetch 请求。这些 fetch 请求使用 HTTPS, 因而请求内容不会泄露,但域名可能会泄露(通过 DNS 查询和请求的 IP 地址)。6. API 描述
6.1. 对 ServiceWorkerRegistration
接口的扩展
ServiceWorkerRegistration/sync
仅在一个当前引擎中可用。
Opera36+Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView49+Samsung Internet4.0+Opera Mobile36+
partial interface ServiceWorkerRegistration {readonly attribute SyncManager ; };sync
属性暴露一个 syncSyncManager,
它具有关联的 service worker registration,由暴露该属性的
ServiceWorkerRegistration
表示。
6.2.
SyncManager
接口
仅在一个当前引擎中可用。
Opera无Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView49+Samsung Internet5.0+Opera Mobile无
[Exposed =(Window ,Worker )]interface {SyncManager Promise <undefined >register (DOMString );tag Promise <sequence <DOMString >>getTags (); };
仅在一个当前引擎中可用。
Opera无Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView49+Samsung Internet5.0+Opera Mobile无
方法在被调用时,必须返回
一个
新 promise promise,并并行地运行以下步骤:
register(tag)
- 令 serviceWorkerRegistration 为
SyncManager的 关联 service worker registration。 - 如果 serviceWorkerRegistration 的 active worker 为 null,则以
InvalidStateError拒绝 promise, 并中止这些步骤。 - 如果用户已禁用后台同步,则以
NotAllowedError拒绝 promise, 并中止这些步骤。 - 令 isBackground 为 true。
-
对于 serviceWorkerRegistration 的源的
service worker clients 中的每个
client:
- 如果 client 的 frame type 为 top-level 或 auxiliary,则将 isBackground 设置为 false。
- 如果 isBackground 为 true,则以
InvalidAccessError拒绝 promise, 并中止这些步骤。 - 令 currentRegistration 为 serviceWorkerRegistration 的 sync registrations 列表中其 tag 等于 tag 的 registration(如果存在),否则为 null。
-
如果 currentRegistration 不为 null:
- 如果 currentRegistration 的 registration state 为 waiting,则将 currentRegistration 的 registration state 设置为 pending。
- 如果 currentRegistration 的 registration state 为 firing,则将 currentRegistration 的 registration state 设置为 reregisteredWhileFiring。
- 解决 promise。
- 如果用户代理当前在线,且 currentRegistration 的 registration state 为 pending,则为 currentRegistration 触发 sync 事件。
-
否则:
- 令 newRegistration 为一个新的 sync registration。
- 将 newRegistration 的关联 tag 设置为 tag。
- 将 newRegistration 的关联 service worker registration 设置为 serviceWorkerRegistration。
- 将 newRegistration 添加到 serviceWorkerRegistration 的 sync registrations 列表。
- 解决 promise。
- 如果用户代理当前在线,则为 newRegistration 触发 sync 事件。
仅在一个当前引擎中可用。
Opera无Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView49+Samsung Internet5.0+Opera Mobile无
方法在被调用时,必须返回 一个新 promise promise,并并行地运行以下步骤:
getTags()
- 令 serviceWorkerRegistration 为
SyncManager的 关联 service worker registration。 - 令 currentTags 为一个新的
sequence。 - 对于 serviceWorkerRegistration 的 sync registrations 列表中的每个 registration,将 registration 的关联 tag 添加到 currentTags。
- 以 currentTags 解决 promise。
6.3. sync 事件
仅在一个当前引擎中可用。
Opera?Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView无Samsung Internet5.0+Opera Mobile?
ServiceWorkerGlobalScope/onsync
在所有当前引擎中可用。
Opera24+Edge79+
Edge (Legacy)无IE无
Firefox for Android44+iOS Safari11.3+Chrome for Android49+Android WebView49+Samsung Internet5.0+Opera Mobile24+
仅在一个当前引擎中可用。
Opera?Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView无Samsung Internet5.0+Opera Mobile?
partial interface ServiceWorkerGlobalScope {attribute EventHandler ; }; [onsync Exposed =ServiceWorker ]interface :SyncEvent ExtendableEvent {(constructor DOMString ,type SyncEventInit );init readonly attribute DOMString ;tag readonly attribute boolean ; };lastChance dictionary :SyncEventInit ExtendableEventInit {required DOMString ;tag boolean =lastChance false ; };
注: SyncEvent
接口表示一个正在触发的 sync registration。如果注册该事件的页面(或 worker)正在运行,
用户代理将在网络连接可用后尽快触发 sync 事件。否则,用户代理应在最早方便的时候运行该事件。
如果 sync 事件失败,用户代理可以决定在其选择的时间重试它。如果用户代理在当前尝试后
不会再尝试此同步,则 lastChance
属性为 true。
lastChance:
self. addEventListener( 'sync' , event=> { if ( event. tag== 'important-thing' ) { event. waitUntil( doImportantThing(). catch ( err=> { if ( event. lastChance) { self. registration. showNotification( "Important thing failed" ); } throw err; }) ); } });
上面的示例通过向用户显示一个
通知来响应 lastChance。
这要求该源具有显示
通知的权限。
在上面的示例中,doImportantThing 是开发者定义的函数。
每当用户代理变为在线时,用户代理应为每个其 registration state 为 pending 的 sync registration 触发 sync 事件。 这些事件可以按任意顺序触发。
要为 sync registration registration 触发 sync 事件,用户代理必须运行以下步骤:
- 断言:registration 的 registration state 为 pending。
- 令 serviceWorkerRegistration 为与 registration 关联的 service worker registration。
- 断言:registration 存在于 与 serviceWorkerRegistration 关联的 sync registrations 列表中。
- 将 registration 的 registration state 设置为 firing。
-
使用
SyncEvent在 serviceWorkerRegistration 上 触发功能事件 "sync", 并具有以下属性:-
仅在一个当前引擎中可用。
Firefox无Safari无Chrome49+
Opera?Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView无Samsung Internet5.0+Opera Mobile?tag - 与 registration 关联的 tag
-
仅在一个当前引擎中可用。
Firefox无Safari无Chrome49+
Opera?Edge79+
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android49+Android WebView无Samsung Internet5.0+Opera Mobile?lastChance - 如果用户代理在此 sync 事件失败时会重试,则为 false;如果当前尝试后不会再进行进一步尝试,则为 true。
然后使用 dispatchedEvent 运行以下步骤:
- 令 waitUntilPromise 为 dispatchedEvent 的 extended lifetime promises 的 全部等待结果。
-
当 waitUntilPromise 兑现时,原子地执行以下步骤:
-
如果 registration 的 state 为 reregisteredWhileFiring:
- 将 registration 的 state 设置为 pending。
- 如果用户代理当前在线,则为 registration 触发 sync 事件。
- 中止这些步骤的其余部分。
- 断言: registration 的 registration state 为 firing。
- 从 serviceWorkerRegistration 的 sync registration 列表中移除 registration。
-
如果 registration 的 state 为 reregisteredWhileFiring:
-
当 waitUntilPromise 被拒绝时,或者如果脚本已因
service worker 的
终止而中止,则原子地执行以下步骤:
-
如果 registration 的 state 为 reregisteredWhileFiring:
- 将 registration 的 state 设置为 pending。
- 如果用户代理当前在线,则为 registration 触发 sync 事件。
- 中止这些步骤的其余部分。
-
如果 dispatchedEvent 的
lastChance属性为 false,则将 registration 的 registration state 设置为 waiting, 并并行地执行以下步骤:- 等待一个用户代理定义的时间长度。
- 如果 registration 的 registration state 不是 waiting,则中止这些子步骤。
- 将 registration 的 registration state 设置为 pending。
- 如果用户代理当前在线,则为 registration 触发 sync 事件。
- 否则,从 serviceWorkerRegistration 的 sync registrations 列表中移除 registration。
-
如果 registration 的 state 为 reregisteredWhileFiring:
-
用户代理可以对 SyncEvent
的生命周期扩展和执行时间施加比一般 ExtendableEvent
更严格的时间限制。特别是,对于其 lastChance
为 true 的事件,可以有显著缩短的时间限制。
用户代理将基于一些由用户代理定义的启发式规则 重试 sync 事件。