I've done a simple service-worker to defer requests that fail for my JS application (following this example) and it works well.
But I still have a problem when requests succeed: the requests are done twice. One time normaly and one time by the service-worker due to the fetch()
call I guess.
It's a real problem because when the client want to save datas, they are saved twice...
Here is the code :
const queue = new workbox.backgroundSync.Queue('deferredRequestsQueue');
const requestsToDefer = [
{ urlPattern: /\/sf\/observation$/, method: 'POST' }
]
function isRequestAllowedToBeDeferred (request) {
for (let i = 0; i < requestsToDefer.length; i++) {
if (request.method && request.method.toLowerCase() === requestsToDefer[i].method.toLowerCase()
&& requestsToDefer[i].urlPattern.test(request.url)) {
return true
}
}
return false
}
self.addEventListener('fetch', (event) => {
if (isRequestAllowedToBeDeferred(event.request)) {
const requestClone = event.request.clone()
const promiseChain = fetch(requestClone)
.catch((err) => {
console.log(`Request added to queue: ${event.request.url}`)
queue.addRequest(event.request)
event.respondWith(new Response({ deferred: true, request: requestClone }))
})
event.waitUntil(promiseChain)
}
})
How to do it well ?
EDIT:
I think I don't have to re-fetch()
the request (because THIS is the cause of the 2nd request) and wait the response of the initial request that triggered the fetchEvent
but I have no idea how to do it. The fetchEvent
seems to have no way to wait (and read) the response.
Am I on the right way ? How to know when the request that triggered the fetchEvent
has a response ?
Using Service Workers, you can build your own custom HTTP responses, including editing their headers. This functionality makes Service Workers extremely powerful—which is why you can understand that they need to serve requests over HTTPS.
Service Workers are a special type of Web Worker with the ability to intercept, modify, and respond to all network requests using the Fetch API. Service Workers can access the Cache API, and asynchronous client-side data stores, such as IndexedDB , to store resources.
Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available).
Service workers are a fundamental part of a PWA. They enable fast loading (regardless of the network), offline access, push notifications, and other capabilities. Users expect apps to start on slow or flaky network connections, or even when offline.
You're calling event.respondWith(...)
asynchronously, inside of promiseChain
.
You need to call event.respondWith()
synchronously, during the initial execution of the fetch
event handler. That's the "signal" to the service worker that it's your fetch
handler, and not another registered fetch
handler (or the browser default) that will provide the response to the incoming request.
(While you're calling event.waitUntil(promiseChain)
synchronously during the initial execution, that doesn't actually do anything with regards to responding to the request—it just ensures that the service worker isn't automatically killed while promiseChain
is executing.)
Taking a step back, I think you might have better luck accomplishing what you're trying to do if you use the workbox.backgroundSync.Plugin
along with workbox.routing.registerRoute()
, following the example from the docs:
workbox.routing.registerRoute(
/\/sf\/observation$/,
workbox.strategy.networkOnly({
plugins: [new workbox.backgroundSync.Plugin('deferredRequestsQueue')]
}),
'POST'
);
That will tell Workbox to intercept any POST
requests that match your RegExp, attempt to make those requests using the network, and if it fails, to automatically queue up and retry them via the Background Sync API.
Piggybacking Jeff Posnick's answer, you need to call event.respondWith()
and include the fetch()
call inside it's async function()
.
For example:
self.addEventListener('fetch', function(event) {
if (isRequestAllowedToBeDeferred(event.request)) {
event.respondWith(async function(){
const promiseChain = fetch(event.request.clone())
.catch(function(err) {
return queue.addRequest(event.request);
});
event.waitUntil(promiseChain);
return promiseChain;
}());
}
});
This will avoid the issue you're having with the second ajax call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With