Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow better composability of fetch handlers #1482

Open
devongovett opened this issue Oct 20, 2019 · 2 comments
Open

Allow better composability of fetch handlers #1482

devongovett opened this issue Oct 20, 2019 · 2 comments

Comments

@devongovett
Copy link

I'm working on implementing default service worker support for Parcel. We'd like to inject some code into service workers at build time to handle Parcel generated assets, but still allow users to write custom code to handle other types of requests themselves (e.g. site content, APIs, etc.).

Currently, if a fetch handler does not call event.respondWith() synchronously within the handler, the event continues propagating to the next handler and so on. This is quite useful for allowing fallback, either to another handler or to the browser. However, if the logic to determine whether to respond to a request or not is asynchronous (e.g. loading something from cache or IDB), then it's impossible. By the time you've decided to respond, it's too late - the request has already been handled by something else.

I would like a way to make a decision about whether to respond to a fetch event asynchronously. I can think of two options, but I'm open to others:

  1. event.continuePropagation() - the opposite of stopPropagation(). You call event.respondWith() as normal, but once you've decided not to actually respond, you continue propagation to the next handler.
self.addEventListener('fetch', event => {
  event.respondWith(async () => {
    if (await shouldRespond(event.request)) {
      return getResponse(event.request);
    }

    event.continuePropagation();
  }());
});
  1. Allow event.respondWith() to be called from within an event.waitUntil() promise. This one might not work because I believe waitUntil causes the worker to wait, not the request, but I could be wrong.
self.addEventListener('fetch', event => {
  event.waitUntil(async () => {
    if (await shouldRespond(event.request)) {
      return event.respondWith(getResponse(event.request));
    }
  }());
});

Previous discussions

This has previously been discussed in 2016 in #836, and probably a few other issues as well. The recommendation at the time seemed to be to use a middleware pattern. This works well if the service worker is all written together, but when tooling like Parcel wants to inject logic into a service worker it falls down. We would like to avoid inventing our own service worker middleware framework or requiring users use a specific library to write their service worker.

See also this twitter thread: https://twitter.com/devongovett/status/1185961634029654016, and our RFC for service worker generation in Parcel: parcel-bundler/parcel#3661.

@jakearchibald
Copy link
Contributor

Isn't the problem here larger than just having two listeners? For instance, what if the developer is trying to install their own service worker? How would you prevent that overriding the one Parcel tries to use?

@devongovett
Copy link
Author

Yeah, another solution is to support multiple service workers on the same page, but this seems like a much harder problem to solve.

We were just going to inject some code into the existing service worker to handle Parcel requests. User code would come afterward. This would be less ideal than multiple service workers since the user service worker wouldn't be able to intercept and modify Parcel requests, but at least they'd continue to be able to handle other requests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants