2017-02-15 128 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