Запросы через сервис-работника выполняются дважды

Я сделал простой сервисный работник, чтобы отложить запросы, которые терпят неудачу для моего приложения JS (после этого примера), и это работает хорошо. Но у меня все еще есть проблема, когда запросы успешно выполняются: запросы выполняются дважды. Один раз обычно и один раз служащим из-за fetch() позвони, наверное.

Это реальная проблема, потому что, когда клиент хочет сохранить данные, они сохраняются дважды...

Вот код:

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

Как это сделать хорошо?

РЕДАКТИРОВАТЬ:

Я думаю, мне не нужно повторно fetch() запрос (потому что это является причиной 2-го запроса) и ждать ответа первоначального запроса, который вызвал fetchEvent но я понятия не имею, как это сделать. fetchEvent кажется, нет возможности ждать (и читать) ответ.

Я на правильном пути? Как узнать, когда запрос вызвал fetchEvent есть ответ?

2 ответа

Ты звонишь event.respondWith(...) асинхронно, внутри promiseChain,

Вам нужно позвонить event.respondWith() синхронно, во время первоначального выполнения fetch обработчик события. Это "сигнал" работнику службы поддержки, что это ваш fetch обработчик, а не другой зарегистрированный fetch обработчик (или браузер по умолчанию), который предоставит ответ на входящий запрос.

(Пока ты звонишь event.waitUntil(promiseChain) синхронно во время первоначального выполнения, это на самом деле ничего не делает в отношении ответа на запрос - это просто гарантирует, что работник службы не будет автоматически убит во время promiseChain выполняется.)

Делая шаг назад, я думаю, что вам, возможно, повезет, выполнив то, что вы пытаетесь сделать, если вы используете workbox.backgroundSync.Plugin вместе с workbox.routing.registerRoute(), следуя примеру из документов:

workbox.routing.registerRoute(
  /\/sf\/observation$/,
  workbox.strategy.networkOnly({
    plugins: [new workbox.backgroundSync.Plugin('deferredRequestsQueue')]
  }),
  'POST'
);

Это скажет Workbox перехватить любой POST запросы, соответствующие вашему RegExp, пытаются выполнить эти запросы с использованием сети, а в случае сбоя автоматически ставят в очередь и повторяют их через API фоновой синхронизации.

Прислушиваясь к ответу Джеффа Посника, вам нужно позвонить event.respondWith() и включать fetch() позвоните внутри это async function(),

Например:

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

Это позволит избежать проблемы, возникающей при втором вызове ajax.

Другие вопросы по тегам