1

正如许多文章在网上解释的那样,我已经使用 Service Worker 在我的 Web 应用程序中实现了 Push WebAPI。现在我需要在 IndexedDB 中存储一些数据,以便在关闭 Web 应用程序时(chrome tab 关闭,后台执行中的 service worker)使它们可用。特别是我想存储一个简单的 url,从中检索通知数据(来自服务器)。

这是我的代码:

    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”上执行“waitUntil”:事件处理程序已经完成。在 IDBRequest.request.onsuccess ( https://192.168.0.102/pushServiceWorker.js:99:23 )

我该如何解决这个问题?

提前致谢

4

1 回答 1

3

首次调用event.waitUntil()事件处理程序时,需要同步完成初始调用。然后,您可以将 Promise 链传递给event.waitUntil(),并在该 Promise 链中执行任意数量的异步操作。

您当前的代码在调用之前调用异步 IndexedDB 回调event.waitUntil(),这就是您看到该错误的原因。

在 Promise 链中包含 IndexedDB 操作的最简单方法是使用包装库,例如idb-keyval,它采用基于回调的 IndexedDB API 并将其转换为基于 Promise 的 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(...)
  );
});
于 2017-02-15T14:50:07.197 回答