Re: [w3c/ServiceWorker] ServiceWorker lifetime and respondWith() with a user-constructed ReadableStream (#882)

Here's what I'd like to see here:

```js
self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request)
  );
});
```

```js
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
  );
});
```

```js
self.addEventListener('fetch', event => {
  event.respondWith((async () => {
    const response = await fetch(event.request);
    return new Response(response.body, {
      headers: response.headers
    });
  })());
});
```

In the above cases, the service worker should be able to terminate once the response has been provided, before the resulting stream has been fully consumed by the browser, without breaking the stream.

So if the response is a 3 hour movie, the SW *does not* need to stay alive while the user watches the 3 hour movie.

Whereas:

```js
self.addEventListener('fetch', event => {
  const responsePromises = [
    caches.match('/shell-start.inc'),
    fetch(event.request.url + '.inc'),
    caches.match('/shell-end.inc')
  ];

  const {writable, readable} = new TransformStream();

  (async () => {
    for await (const response of responsePromises) {
      await response.body.pipeTo(writable, {preventClose: true});
    }
    writable.close();
  })();

  event.respondWith(
    new Response(readable, {
      headers: {'Content-Type': 'text/html'}
    })
  );
});
```

…this may not work, as the SW may terminate before all of the `pipeTo`s are called. When a stream is GC'd that cannot complete, we need to figure out if this is an error or a cancellation. My gut feeling is error, as if a server simply cut off its response & reset the connection, but I'd like to hear from @domenic.

To avoid the above, you'd need to do:

```js
self.addEventListener('fetch', event => {
  const responsePromises = [
    caches.match('/shell-start.inc'),
    fetch(event.request.url + '.inc'),
    caches.match('/shell-end.inc')
  ];

  const {writable, readable} = new TransformStream();

  event.waitUntil((async () => {
    for await (const response of responsePromises) {
      await response.body.pipeTo(writable, {preventClose: true});
    }
    writable.close();
  })());

  event.respondWith(
    new Response(readable, {
      headers: {'Content-Type': 'text/html'}
    })
  );
});
```

Now the `waitUntil` explicitly holds the SW open for all the stream operations.

I'm curious about this case:

```js
event.waitUntil((async () => {
  for (let i, response; response = await responsePromises[i]; i++) {
    const isLast = i == responsePromises.length - 1;
    if (isLast) {
      response.body.pipeTo(writable);
    }
    else {
      await response.body.pipeTo(writable, {preventClose: true});
    }
  }
})());
```

Here `waitUntil` holds the SW open until `pipeTo` has been called for the last stream, but it doesn't hold it open until the whole stream has been consumed. However, at this point no further JS needs to be executed to complete the stream. We've piped a behind-the-scenes stream to an identity stream, and the identity stream will auto-close when piping completes.

Should the SW be able to close, allowing the stream to complete successfully? @domenic?

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/ServiceWorker/issues/882#issuecomment-262754779

Received on Thursday, 24 November 2016 11:29:22 UTC