What;s more expensive, single persistent Fetch request that might stall yet stay alive, or multiple Fetch requests?

866 views
Skip to first unread message

guest271314

unread,
Jul 12, 2023, 9:43:48 AM7/12/23
to blink-network-dev
What;s more expensive, single persistent Fetch request that might stall yet stay alive, or multiple Fetch requests?

Adam Rice

unread,
Jul 12, 2023, 9:53:10 AM7/12/23
to guest271314, blink-network-dev
That entirely depends on the application. If possible I recommend implementing both and measuring them.

In the most general terms, holding a connection open will waste memory but may use less CPU than creating new ones as needed.

On Wed, 12 Jul 2023 at 22:43, guest271314 <guest...@gmail.com> wrote:
What;s more expensive, single persistent Fetch request that might stall yet stay alive, or multiple Fetch requests?

--
You received this message because you are subscribed to the Google Groups "blink-network-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-network-...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-network-dev/41baba90-6927-4947-9eec-25e0fee3fad0n%40chromium.org.

guest271314

unread,
Jul 12, 2023, 8:07:01 PM7/12/23
to Adam Rice, blink-network-dev
After reading the upload streaming article I thought that fetch could be used for bi-directional streaming using a single request, essentially WebTransport streams using Fetch. 

So the connection remains persisten AFAICT, a dedicated streaming message channel. Where we can of course substitute streaming other data for echoing the input.

The only cost I observed was the request being stalled in Network panel of DevTools. I did a little reading on the subject matter and the opinions are wide ranging, so I thought I'd ask the experts 

<!DOCTYPE html>

<html>
  <head> </head>

  <body>
    <input type="text" />
    <output></output>
    <script>
      async function halfDuplexStream() {
        let controller;
        const stream = new ReadableStream({
          start(_) {
            return (controller = _);
          },
        }).pipeThrough(new TextEncoderStream());
        const output = document.querySelector('output');
        const input = document.querySelector('input');
        input.onchange = (e) => {
          controller.enqueue(e.target.value);
        };

        input.onselect = (e) => {
          input.value = '';
        };

        fetch('./?stream', {
          method: 'POST',
          headers: { 'Content-Type': 'text/plain' },
          body: stream,
          duplex: 'half',
        })
          .then((r) =>
            r.body.pipeThrough(new TextDecoderStream()).pipeTo(
              new WritableStream({
                write(value) {
                  output.textContent = value;
                },
                close() {
                  console.log('Stream closed');
                },
                abort(reason) {
                  console.log({ reason });
                },
              })
            )
          )
          .then(console.log)
          .catch(console.warn);
      }
      navigator.serviceWorker
        .getRegistrations()
        .then((r) => Promise.all(r.map((s) => s.unregister())))
        .then(() =>
          navigator.serviceWorker.register('sw.js', {
            scope: './',
          })
        )
        .then((s) => {
          return new Promise((resolve) => {
            navigator.serviceWorker.addEventListener(
              'controllerchange',
              (e) => {
                console.log(e);
                resolve();
              },
              { once: true }
            );
          });
        })
        .then(halfDuplexStream)
        .catch(console.error);
    </script>
  </body>
</html>

A ServiceWorker which can be substituted for Deno's internal serveTls server which implements Fetch, and ServiceWorker style server with half-duplex streaming

self.addEventListener('install', (event) => {
  console.log(event);
  event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate', async (event) => {
  console.log(event);
  event.waitUntil(self.clients.claim());
  console.log(await self.clients);
});

onfetch = (e) => {
  if (e.request.url.includes('stream')) {
    let client = clients.get(e.clientId);
    e.respondWith(
      new Response(
        e.request.body
          .pipeThrough(new TextDecoderStream())
          .pipeThrough(
            new TransformStream({
              transform(value, c) {
                c.enqueue(value.toUpperCase());
              },
              flush() {
                console.log('flush');
              },
            })
          )
          .pipeThrough(new TextEncoderStream())
      )
    );
  }
};

Reply all
Reply to author
Forward
0 new messages