當咱們在一些地下停車場,或者在火車上、電梯等沒法避免的信號不穩定的場所,使用網站應用處理一些表單操做或者上傳數據的操做時,面臨的將是網絡鏈接錯誤的響應,使用戶的操做白費。html
而此刻 PWA 的 Sync API 就很好的解決了這個問題,讓用戶處理一些數據上傳的操做時,無需關係網絡環境,全部相關操做均會完成。Sync API 也是 PWA 離線裏面的重要一環,下面就說一塊。數組
SyncManager 接口提供了用於註冊和獲取 Sync 註冊的接口。Sync 是一個簡單且很是實用的功能。瀏覽器
經過 ServiceWorkerRegistration 接口的 sync 進行獲取:網絡
navigator.serviceWorker.ready.then(reg => {
console.log(reg.sync)
})
複製代碼
SyncManager.registerasync
用於註冊一個 sync tag,tag 按照本身的需求設置。fetch
語法:網站
SyncManager.register(DOMString tag).then(function(void) { ... })
複製代碼
返回 void 的 Promise。ui
SyncManager.getTagsspa
用於獲取已註冊且未完成的 sync tag(完成後的 sync tag 會自動今後列表中刪除)。.net
語法:
SyncManager.getTags().then(function(tags[]) { ... })
複製代碼
返回註冊 syn tag 的字符串數組的 Promise。
onsync
註冊後的 Sync tag 會觸發 ServiceWorkerGlobalScope 下的 onsync 事件。
此事件值包含兩個屬性:
tag
:返回觸發這次事件的註冊 Sync tag 的 tag 值。lastChance
:若是瀏覽器在嘗試屢次後還未成功,當 lastChance 爲 true
時表示再也不嘗試,且這次 sync tag 刪除。從註冊一個 Sync tag 到這個 Sync tag 完成,會經歷三個階段:
SyncManager.register(tag)
後,會當即註冊 sync,並將註冊後的 sync tag 放入 sync 的註冊列表中(能夠經過SyncManager.getTags()
獲取到),而後會判斷當前的網絡環境,只有網絡在線的狀況下,註冊的 Sync 纔會發出 sync 事件,而後當 SyncEvent.waitUntil()
中 Promise 爲 reject 時將會週期性的觸發 onsync 事件,直到不爲 reject 纔會完成 Sync tag,而後將相關 tag 清除。
在 Chrome 下,當 SyncEvent.waitUntil()
中的參數值一直爲 Promise reject 時,會最多觸發三次 onsync 事件,每次的週期時間至少爲 5 分鐘。
注意:sync 事件中的處理結果必須放在 SyncEvent.waitUntil() 中,不然會當即完成 Sync。
注意:上次中的重試次數和週期時間是 Chrome 環境下的體現,具體次數和週期標準中未規範,能夠 e.lastChance 來判讀,處理最後一次的相關邏輯。
能夠經過 DevTools 裏的 Background Services 查看 Sync 的執行過程:
SyncManager 自己只是一個簡單的 API,sync 事件中也只有兩個只讀屬性,因此基於 Sync 來作的同步數據,比較好的方式搭配 IndexDB 來實現,下面兩個場景也是基於 IndexDB。
這種場景下,相關場景的數據請求先寫入 IndexDB 中,而後註冊 Sync,在 onsync 中根據相關 tag 來處理 IndexDB 中的數據請求。
下面是一個聊天應用的場景
index.html:
btnSend.addEventListener('click', async () => {
await db.add('chatList', { msg, time, useId});
reg = await navigator.serviceWorker.ready;
reg.sync.register('send_chat');
})
複製代碼
sw.js
self.addEventListener('sync', e => {
e.tag == 'send_chat' && e.waitUntil(new Promise.then(async (res, rej) => {
var allData = await db.getAll('chatList');
return Promise.all(allData.map(data => fetch(data)));
}))
})
複製代碼
這個場景能夠針對於某些特定請求,先讓它正常發送網絡請求,若是失敗則將失敗的請求放到相關的 IndexDB 中,並設定這條網絡請求可嘗試的有效期,有效期內均會重拾。
關於 sync 的週期上面也說過,在 Chrome 下最多嘗試三次,本場景下的這種需求,須要相關的 sync tag 一直處理可用狀態,因此須要對這一層進行修改知足需求。
例如點贊場景
index.html
btnLike.addEventListener('click', () => {
reg = await navigator.serviceWorker.ready;
reg.sync.register('like’);
fetch(data).catch(e => {
db.add('likeList', {data, lastTime: 12938749138}); // 有效期時間戳
})
})
複製代碼
sw.js
self.addEventListener("sync", e => {
if (e.tag == "send_chat") {
e.waitUntil(
new Promise.then(async (res, rej) => {
while (db.get("likeList")[0]) {
var data = db.get("likeList")[0];
try {
if(data.lastTime > Date.now()) {
db.remove('likeList', data)
} else {
await fetch(data);
db.remove('likeList', data)
}
} catch (err) {
if(e.lastChance == true) { // 若是最後一次嘗試機會,則從新註冊,保證一直有效
self.registration.sync.register('like')
}
}
}
})
);
}
});
複製代碼
注意:上面代碼中的 db 爲模擬的僞代碼。
經過 Sync API 的支持,網站應用能夠較大的離線化,提高用戶的體驗度和應用的可靠性。這樣用戶在網絡鏈接失敗的狀況下也能上傳表單、點贊評論文章、發送消息等等,爲用戶帶來積極的影響。也意味着 Web 應用更加接近原生應用的體驗效果。
博客名稱:王樂平博客
CSDN博客地址:blog.csdn.net/lecepin