我有一个Service Worker,它接收来自Firebase FCM的推送消息。它们会导致显示或取消通知。用户可以拥有多个设备(取消的目的是什么:当用户已经对通知A采取行动时,我试图在所有设备上将其解除)。
我遇到的问题是其中一个用户的设备处于脱机状态或完全关闭。设备上线后,firebase会提供之前无法提供的所有消息。所以,例如,你得到:
SW迅速接收这些消息。问题是取消通知比显示通知要快得多(~2ms vs 16ms)。因此,在第一条(或第二条)消息实际创建通知之前处理第4条消息,结果是通知未被取消。
//编辑:下面的重大编辑问题。添加了示例代码并打破了我的问题。还编辑了标题以更好地反映我的实际基本问题。
我尝试将消息推送到队列中并逐个处理它们。事实证明这可能会变得有点复杂,因为SW中的所有内容都是异步的,更糟糕的是,当浏览器认为SW完成其工作时,它可以随时被杀死。我试图以持久的方式存储队列,但由于LocalStorage在SW中不可用,我需要使用异步IndexedDB API。更多可能导致问题的异步调用(例如丢失项目)。
event.waitUntil
也可能认为我的工作人员在实际完成之前就完成了,因为我没有正确地通过火炬传递。从承诺到承诺......
这是我尝试过的(很多)简化代码:
// Use localforage, simplified API for IndexedDB
importScripts("localforage.min.js");
// In memory..
var mQueue = []; // only accessed through get-/setQueue()
var mQueueBusy = false;
// Receive push messages..
self.addEventListener('push', function(event) {
var data = event.data.json().data;
event.waitUntil(addToQueue(data));
});
// Add to queue
function addToQueue(data) {
return new Promise(function(resolve, reject) {
// Get queue..
getQueue()
.then(function(queue) {
// Push + store..
queue.push(data);
setQueue(queue)
.then(function(queue){
handleQueue()
.then(function(){
resolve();
});
});
});
});
}
// Handle queue
function handleQueue(force) {
return new Promise(function(resolve, reject) {
// Check if busy
if (mQueueBusy && !force) {
resolve();
} else {
// Set busy..
mQueueBusy = true;
// Get queue..
getQueue()
.then(function(queue) {
// Check if we're done..
if (queue && queue.length<=0) {
resolve();
} else {
// Shift first item
var queuedData = queue.shift();
// Store before continuing..
setQueue(queue)
.then(function(queue){
// Now do work here..
doSomething(queuedData)
.then(function(){
// Call handleQueue with 'force=true' to go past (mQueueBusy)
resolve(handleQueue(true));
});
});
}
});
}
});
}
// Get queue
function getQueue() {
return new Promise(function(resolve, reject) {
// Get from memory if it's there..
if (mQueue && mQueue.length>0) {
resolve(mQueue);
}
// Read from indexed db..
else {
localforage.getItem("queue")
.then(function(val) {
var queue = (val) ? JSON.parse(val) : [];
mQueue = queue;
resolve(mQueue);
});
}
});
}
// Set queue
function setQueue(queue) {
return new Promise(function(resolve, reject) {
// Store queue to memory..
mQueue = queue;
// Write to indexed db..
localforage.setItem("queue", mQueue)
.then(function(){
resolve(mQueue);
});
});
}
// Do something..
function doSomething(queuedData) {
return new Promise(function(resolve, reject) {
// just print something and resolve
console.log(queuedData);
resolve();
});
}
我的问题的简短版本 - 考虑到我的特定用例 - 是:如何在不使用更多异步API的情况下同步处理推送消息?
如果我将这些问题分成多个:
resolve(handleQueue())
内调用handleQueue()
来保持它的运行状态?或者我应该return handleQueue()
吗?还是......?只是为了理解&#34;为什么不使用collapse_key&#34;:它是一个聊天应用程序,每个聊天室都有它自己的标签。用户可以参与4个以上的聊天室,因为firebase将collapse_keys的数量限制为4,我无法使用它。
答案 0 :(得分:2)
因此,我要走出困境,并说向IDB序列化事情可能有点过分。只要您在解决传递给event.waitUntil()
的承诺之前等待所有待处理的工作完成,服务工作者就应该保持活跃状态。 (如果完成这项工作需要几分钟,那么服务工作者无论如何都有可能被杀,但对于你描述的内容,我说这种风险很低。)
以下是我如何构建代码的粗略草图,利用当前支持服务工作者的所有浏览器中的原生async
/await
支持。
(我还没有真正测试过这一点,但从概念上讲,我认为这听起来很棒。)
// In your service-worker.js:
const isPushMessageHandlerRunning = false;
const queue = [];
self.addEventListener('push', event => {
var data = event.data.json().data;
event.waitUntil(queueData(data));
});
async function queueData(data) {
queue.push(data);
if (!isPushMessageHandlerRunning) {
await handlePushDataQueue();
}
}
async function handlePushDataQueue() {
isPushMessageHandlerRunning = true;
let data;
while(data = queue.shift()) {
// Await on something asynchronous, based on data.
// e.g. showNotification(), getNotifications() + notification.close(), etc.
await ...;
}
isPushMessageHandlerRunning = false;
}