Monday, June 3, 2024
4
rated 0 times [  11] [ 7]  / answers: 1 / hits: 35568  / 9 Years ago, wed, july 29, 2015, 12:00:00

I am really new to JavaScript and promises and to be honest I don't fully understand how promises work so I need some help.



I am using Google Cloud Messaging to push notifications from my site to my users. When users receive a notification and clicks on it, it opens a URL stored in a IndexedDB.



importScripts('IndexDBWrapper.js');
var KEY_VALUE_STORE_NAME = 'key-value-store', idb;

function getIdb() {
if (!idb) {
idb = new IndexDBWrapper(KEY_VALUE_STORE_NAME, 1, function (db) {
db.createObjectStore(KEY_VALUE_STORE_NAME);
});
}
return idb;
}

self.addEventListener('notificationclick', function (event) {
console.log('On notification click: ', event);
event.notification.close();
event.waitUntil(getIdb().get(KEY_VALUE_STORE_NAME, event.notification.tag).then(function (url) {
var redirectUrl = '/';
if (url) redirectUrl = url;
return clients.openWindow(redirectUrl);
}));
});


So in the code above, I know that the getIdb()...then() is a promise, but is the event.waitUntil also a promise?



The problem with the above code is that it opens a instance of Chrome every time the notification is clicked and I would prefer that it would utilize an existing instance if available. The following does just that:



self.addEventListener('notificationclick', function(event) {  
console.log('On notification click: ', event.notification.tag);
event.notification.close();
event.waitUntil(
clients.matchAll({
type: window
})
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow('/');
}
})
);
});


However, now I have two promises, getIdb and clients.matchAll and I really have no idea how to combine the two promises and the two sets of code. Any help would be greatly appreciated. Thanks!



For reference, here is IndexDBWrapper.js:



'use strict';

function promisifyRequest(obj) {
return new Promise(function(resolve, reject) {
function onsuccess(event) {
resolve(obj.result);
unlisten();
}
function onerror(event) {
reject(obj.error);
unlisten();
}
function unlisten() {
obj.removeEventListener('complete', onsuccess);
obj.removeEventListener('success', onsuccess);
obj.removeEventListener('error', onerror);
obj.removeEventListener('abort', onerror);
}
obj.addEventListener('complete', onsuccess);
obj.addEventListener('success', onsuccess);
obj.addEventListener('error', onerror);
obj.addEventListener('abort', onerror);
});
}

function IndexDBWrapper(name, version, upgradeCallback) {
var request = indexedDB.open(name, version);
this.ready = promisifyRequest(request);
request.onupgradeneeded = function(event) {
upgradeCallback(request.result, event.oldVersion);
};
}

IndexDBWrapper.supported = 'indexedDB' in self;

var IndexDBWrapperProto = IndexDBWrapper.prototype;

IndexDBWrapperProto.transaction = function(stores, modeOrCallback, callback) {
return this.ready.then(function(db) {
var mode = 'readonly';

if (modeOrCallback.apply) {
callback = modeOrCallback;
}
else if (modeOrCallback) {
mode = modeOrCallback;
}

var tx = db.transaction(stores, mode);
var val = callback(tx, db);
var promise = promisifyRequest(tx);
var readPromise;

if (!val) {
return promise;
}

if (val[0] && 'result' in val[0]) {
readPromise = Promise.all(val.map(promisifyRequest));
}
else {
readPromise = promisifyRequest(val);
}

return promise.then(function() {
return readPromise;
});
});
};

IndexDBWrapperProto.get = function(store, key) {
return this.transaction(store, function(tx) {
return tx.objectStore(store).get(key);
});
};

IndexDBWrapperProto.put = function(store, key, value) {
return this.transaction(store, 'readwrite', function(tx) {
tx.objectStore(store).put(value, key);
});
};

IndexDBWrapperProto.delete = function(store, key) {
return this.transaction(store, 'readwrite', function(tx) {
tx.objectStore(store).delete(key);
});
};

More From » push-notification

 Answers
10

event.waitUntil() takes a promise - this allows the browser to keep your worker alive until you've finished what you want to do (i.e. until the promise that you gave to event.waitUntil() has resolved).



As the other answer suggests, you can use Promise.all() within event.waitUntil. Promise.all() takes an array of promises and returns a promise, so you can call then on it. Your handling function will get an array of promise results when all of the promises you've provided to Promise.all have resolved. Your code will then look something like this (I haven't actually tested this, but it should be close):



self.addEventListener('notificationclick', function (event) {
event.notification.close();
event.waitUntil(Promise.all([
getIdb().get(KEY_VALUE_STORE_NAME, event.notification.tag),
clients.matchAll({ type: window })
]).then(function (resultArray) {
var url = resultArray[0] || /;
var clientList = resultArray[1];
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow(url);
}
}));
});

[#65626] Monday, July 27, 2015, 9 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
megb

Total Points: 230
Total Questions: 113
Total Answers: 100

Location: Tokelau
Member since Sun, May 7, 2023
1 Year ago
;