通过service-worker发起的请求会被执行两次

5
我已经为我的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() 请求(因为这是第二个请求的原因),也不用等待触发fetchEvent的初始请求的响应,但我不知道如何做。fetchEvent似乎没有等待(和读取)响应的方法。
我走对了吗?如何知道触发fetchEvent的请求何时有响应?

双重请求的原因是客户端发出请求,获取侦听器看到它并根据您的指示发出第二个请求。为了防止这种情况,请从catch中删除respond with,在waitUntil上方添加event.respondWith(promiseChain),然后完成。因此,您的侦听器观察、接管、发出请求、发送返回调整后的promise给客户端。 - Warren
2个回答

2
您正在异步调用event.respondWith(...),在promiseChain中。

您需要在fetch事件处理程序的初始执行期间同步调用event.respondWith()。这是向服务工作者发出的“信号”,表明它是您的fetch处理程序,而不是另一个已注册的fetch处理程序(或浏览器默认值),该处理程序将为传入请求提供响应。

(虽然您在初始执行期间同步调用了event.waitUntil(promiseChain),但这实际上与响应请求无关 - 它只确保服务工作者在执行promiseChain时不会被自动终止。)

退一步讲,如果您使用workbox.routing.registerRoute()workbox.backgroundSync.Plugin,并按照文档中的示例操作,我认为您可能会更容易实现您想要做的事情:

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

那将告诉 Workbox 拦截与您的 RegExp 匹配的任何 POST 请求,尝试使用网络进行这些请求,如果失败,自动通过后台同步 API 将它们排队并重试。

好的,但是使用backgroundSync插件,我的应用程序可以检测到请求失败但被service-worker推迟吗? - Victor Castro

2

参考Jeff Posnick的回答,你需要调用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调用中遇到的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接