0

Using lodash debounced functions in ServiceWorker is OK for Firefox. They wait for the assigned timeout and the timer resets if the debounced function is called again.

However, for Chrome, nothing works as expected: the debounced function calls the inner function as soon as called and will not wait for resets in the timer.

Has anyone ran into this problem? Any workarounds?

I am using it to wait for idle user (no more fetches for some time) to send logged analytics.

Jeff Posnick
  • 53,580
  • 14
  • 141
  • 167
Eduardo Poço
  • 2,819
  • 1
  • 19
  • 27

1 Answers1

2

It's a little difficult to debug this fully without seeing your example code, but a general rule of thumb would be that if you're scheduling asynchronous work that you want done outside of the synchronous execution of a service worker event handler, you need to

  • schedule that work from inside of a ExtendableEvent and
  • wrap the work by passing a promise for its completion to the waitUntil() method of that ExtendableEvent.

(FetchEvent is an ExtendableEvent.)

If you fail to do that, then the browser does not guarantee that your asynchronous task will complete before the service worker thread is killed. Different browsers are more or less aggressive about terminating the service worker thread, so that might explain the different behavior you're seeing.

lodash's debounce() does not offer built-in support for promises, so you'll either have to wrap things yourself, or find a suitable alternative library. Just note that whatever approach you use, make sure that you use promises that will eventually resolve, even if the operation is debounced. Otherwise, if you pass a promise that doesn't resolve to waitUntil(), you'll end up keeping the service worker alive much too long.

Putting aside the actual implementation of a promise-returning debounceWithPromises(), the general structure would look like:

self.addEventListener('fetch', (event) => {
  if (/* some criteria is met */) {
    // As soon as the promise passed to respondWith() resolves,
    // the response will start being fed to the client.
    event.respondWith(generateResponse(event);

    // Independent of the response generation, the promise returned
    // by the hypothetical debounceWithPromises() will keep the
    // service worker alive until it resolves or rejects (or until a
    // browser-specific maximum lifetime is reached).
    event.waitUntil(debounceWithPromises(logStats));
  }
});
Jeff Posnick
  • 53,580
  • 14
  • 141
  • 167
  • Everything makes sense now, but I thought a killed serviceworker would end up not sending analytics, instead of skipping the waiting time – Eduardo Poço Apr 26 '21 at 19:23
  • I'm not 100% sure about that difference in behavior, but relying on `event.waitUntil()` for this sort of thing is still important. – Jeff Posnick Apr 26 '21 at 19:28