2017-02-15 44 views
1

我在Web應用程序中使用Service Worker實現了推送WebAPI,正如許多文章在Web上解釋的一樣。 現在我需要在IndexedDB中存儲一些數據,以便在關閉web應用程序時關閉它們(chrome選項卡關閉,服務員在後臺執行)。 特別是我想存儲一個簡單的URL從檢索通知數據(從服務器)。推WebAPI + IndexedDB + ServiceWorker

這裏是我的代碼:

self.addEventListener("push", (event) => { 
    console.log("[serviceWorker] Push message received", event); 

    notify({ event: "push" }); // This notifies the push service for handling the notification 

    var open = indexedDB.open("pushServiceWorkerDb", 1); 
    open.onsuccess =() => { 
     var db = open.result; 
     var tx = db.transaction("urls"); 
     var store = tx.objectStore("urls"); 
     var request = store.get("fetchNotificationDataUrl"); 

     request.onsuccess = (ev) => { 
      var fetchNotificationDataUrl = request.result; 
      console.log("[serviceWorker] Fetching notification data from ->", fetchNotificationDataUrl); 

      if (!(!fetchNotificationDataUrl || fetchNotificationDataUrl.length === 0 || !fetchNotificationDataUrl.trim().length === 0)) { 
       event.waitUntil(
        fetch(fetchNotificationDataUrl, { 
         credentials: "include" 
        }).then((response) => { 
         if (response.status !== 200) { 
          console.log("[serviceWorker] Looks like there was a problem. Status Code: " + response.status); 
          throw new Error(); 
         } 

         return response.json().then((data) => { 
          if (!data) { 
           console.error("[serviceWorker] The API returned no data. Showing default notification", data); 
           //throw new Error(); 
           showDefaultNotification({ url: "/" }); 
          } 

          var title = data.Title; 
          var message = data.Message; 
          var icon = data.Icon; 
          var tag = data.Tag; 
          var url = data.Url; 

          return self.registration.showNotification(title, { 
           body: message, 
           icon: icon, 
           tag: tag, 
           data: { 
            url: url 
           }, 
           requireInteraction: true 
          }); 
         }); 
        }).catch((err) => { 
         console.error("[serviceWorker] Unable to retrieve data", err); 

         var title = "An error occurred"; 
         var message = "We were unable to get the information for this push message"; 
         var icon = "/favicon.ico"; 
         var tag = "notification-error"; 
         return self.registration.showNotification(title, { 
          body: message, 
          icon: icon, 
          tag: tag, 
          data: { 
           url: "/" 
          }, 
          requireInteraction: true 
         }); 
        }) 
       ); 
      } else { 
       showDefaultNotification({ url: "/" }); 
      } 
     } 
    }; 
}); 

不幸的是,當我收到新推的事件這是行不通的,顯示出此異常:對​​未能執行「最好推遲」:

未捕獲拋出:DOMException 'ExtendableEvent':事件處理程序已經完成。 在IDBRequest.request.onsuccess(https://192.168.0.102/pushServiceWorker.js:99:23

我怎樣才能解決這個問題?

在此先感謝

回答

2

event.waitUntil()最初的呼叫需要是同步完成的第一次調用事件處理函數時。然後,您可以將承諾鏈傳遞給event.waitUntil(),並在承諾鏈內執行任意數量的異步操作。

您的當前代碼在調用event.waitUntil()之前會調用異步IndexedDB回調,這就是您看到該錯誤的原因。

在承諾鏈中包含IndexedDB操作的最簡單方法是使用包裝庫,如idb-keyval,它接受基於回調的IndexedDB API並將其轉換爲基於承諾的API。

那麼你的代碼可能是這樣的:

self.addEventListener('push', event => { 
    // Call event.waitUntil() immediately: 
    event.waitUntil(
    // You can chain together promises: 
    idbKeyval.get('fetchNotificationDataUrl') 
     .then(url => fetch(url)) 
     .then(response => response.json()) 
     .then(json => self.registration.showNotification(...) 
); 
}); 
+0

對於那些有興趣的我用self.importScripts( '/庫/ IDB-KEYVAL /距離/ IDB-KEYVAL-min.js');從服務工作者使用idb-keyval – Androidian