PWA(Progressive Web App)入門系列:Sync 後臺同步

前言

當咱們在一些地下停車場,或者在火車上、電梯等沒法避免的信號不穩定的場所,使用網站應用處理一些表單操做或者上傳數據的操做時,面臨的將是網絡鏈接錯誤的響應,使用戶的操做白費。html

而此刻 PWA 的 Sync API 就很好的解決了這個問題,讓用戶處理一些數據上傳的操做時,無需關係網絡環境,全部相關操做均會完成。Sync API 也是 PWA 離線裏面的重要一環,下面就說一塊。數組


SyncManager API

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 完成,會經歷三個階段:

  • Registered sync:註冊 sync。
  • Dispatched sync event:發出 sync 事件。
  • Sync completed:Sync 完成。

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。

1. 徹底 Sync 化數據請求傳輸

這種場景下,相關場景的數據請求先寫入 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)));
  }))
})
複製代碼

2. 失敗請求的 Sync 化

這個場景能夠針對於某些特定請求,先讓它正常發送網絡請求,若是失敗則將失敗的請求放到相關的 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

知識共享許可協議
本做品採用 知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。
相關文章
相關標籤/搜索