I have a Worker which starts on a page opening. The worker opens a transaction, checks to see if a value is present on a record and if not needs to make a fetch call to get the value.
The issue I have is that I do this in a read/write transaction but the fetch is async which commits the transaction before the fetch is returned. If I could be guaranteed only a single page accessing the logic at the same time then I would just do it without a transaction, eg get the data, check it, if required get value and update. But I cant be guaranteed a single page.
So the question is how do I guaranteed that the second page doesn't start the process of getting a new value once the first page has started the process?
async function start(message) {
var result = await new Promise(async (resolve, reject) => {
let transaction = db.transaction(["Client"], "readwrite")
let objectStore = transaction.objectStore("Client");
let request = objectStore.get(message.client_name);
request.onerror = (event) => {
reject(event);
};
request.onsuccess = async () => {
try {
let data = request.result
if (!data) {
data = {
client_name: message.client_name,
sequence: 0,
api_base_url: message.api_base_url || 'https://api.local'
}
}
if (!data.last_access || dateDiff(Date.parse(data.last_access), new Date(), "m") > 20) {
let response = await fetch(data.api_base_url + '/v1/hello');
let responseData = await response.json();
data.id = responseData.id;
data.last_access = responseData.dt;
}
objectStore.put(data); // <-- Here is where it errors
resolve(data.sequence);
} catch (error) {
reject(error);
}
}
});
return result;
}
There's currently no good way to do this.
If you're using Dexie, it has a waitFor
function which does let you keep a transaction open while waiting for another promise to resolve. But...
Use with caution as it may put unnecessary CPU load on the browser. A separate task will keep the transaction alive by propagating dummy-requests on the transaction while the given promise is being executed.
If you don't use Dexie, you could implement the same thing yourself. Keep making requests while checking if your fetch promise is resolved yet.
It would be better to find some way to not have to keep the transaction active during the fetch. Like maybe store a flag in localStorage to prevent the 2nd call from doing anything? Or cancel the 1st fetch if a 2nd one starts before the 1st finishes? I'm not sure exactly what you're trying to do, but usually there is some way to work around this limitation of IndexedDB.