最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Requests through service-worker are done twice - Stack Overflow

programmeradmin0浏览0评论

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)
  }
})

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 ?

Share Improve this question edited Mar 16, 2022 at 11:49 VLAZ 29.1k9 gold badges63 silver badges84 bronze badges asked May 2, 2018 at 7:34 Victor CastroVictor Castro 1,2421 gold badge22 silver badges41 bronze badges 1
  • reason for double req - client makes req, fetch listener sees it and makes a second req (as per ur instructions). To prevent this, remove respond with from inside the catch, above waitUntil add event.respondWith(promiseChain) and your done. So your listener watches, takes over, makes req, sends returns adjusted promise to client. – Warren Commented Feb 4, 2020 at 23:47
Add a ment  | 

2 Answers 2

Reset to default 3

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 ining 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 acplishing 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.strategyworkOnly({
    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.

发布评论

评论列表(0)

  1. 暂无评论