推送 WebAPI + IndexedDB + ServiceWorker
Push WebAPI + IndexedDB + ServiceWorker
我已经使用 Service Worker 在我的 Web 应用程序中实现了 Push WebAPI,正如 Web 上的许多文章所解释的那样。
现在我需要在 IndexedDB 中存储一些数据,以便在关闭 Web 应用程序(chrome 选项卡关闭,后台执行中的服务工作者)时使它们可用。
特别是我想存储一个简单的 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: "/" });
}
}
};
});
不幸的是,当我收到一个新的推送事件时,它不起作用,显示了这个异常:
Uncaught DOMException: Failed to execute 'waitUntil' on 'ExtendableEvent': The event handler is already finished.
at IDBRequest.request.onsuccess (https://192.168.0.102/pushServiceWorker.js:99:23)
我该如何解决这个问题?
提前致谢
首次调用事件处理程序时,需要同步完成对 event.waitUntil()
的初始调用。然后,您可以将承诺链传递给 event.waitUntil()
,并在该承诺链内执行任意数量的异步操作。
您当前的代码在调用 event.waitUntil()
之前调用异步 IndexedDB 回调,这就是您看到该错误的原因。
在承诺链中包含 IndexedDB 操作的最简单方法是使用包装库,如 idb-keyval
,它采用 callback-based IndexedDB API 并将其转换为 promise-based 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(...)
);
});
我已经使用 Service Worker 在我的 Web 应用程序中实现了 Push WebAPI,正如 Web 上的许多文章所解释的那样。 现在我需要在 IndexedDB 中存储一些数据,以便在关闭 Web 应用程序(chrome 选项卡关闭,后台执行中的服务工作者)时使它们可用。 特别是我想存储一个简单的 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: "/" });
}
}
};
});
不幸的是,当我收到一个新的推送事件时,它不起作用,显示了这个异常:
Uncaught DOMException: Failed to execute 'waitUntil' on 'ExtendableEvent': The event handler is already finished. at IDBRequest.request.onsuccess (https://192.168.0.102/pushServiceWorker.js:99:23)
我该如何解决这个问题?
提前致谢
首次调用事件处理程序时,需要同步完成对 event.waitUntil()
的初始调用。然后,您可以将承诺链传递给 event.waitUntil()
,并在该承诺链内执行任意数量的异步操作。
您当前的代码在调用 event.waitUntil()
之前调用异步 IndexedDB 回调,这就是您看到该错误的原因。
在承诺链中包含 IndexedDB 操作的最简单方法是使用包装库,如 idb-keyval
,它采用 callback-based IndexedDB API 并将其转换为 promise-based 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(...)
);
});