v157 · javascript · service workers

Streaming download relay

The use case that motivated the change. A service worker fetches a real same-origin asset, counts bytes off the response’s ReadableStream, and reports progress to this page — building a genuine ProgressEvent to do it where the platform allows, and falling back to a hand-rolled object where it does not. The label above the bar says which one produced the numbers.

Checking service worker support…

Relay a download through the worker

idle

progress values came from:nothing yet

Press Start relay to register the worker and stream an asset through it.
(no messages yet)

what the worker does

const response = await fetch(url);
const total = Number(response.headers.get("content-length")) || 0;
const reader = response.body.getReader();
let loaded = 0;

for (;;) {
  const { done, value } = await reader.read();
  if (done) break;
  loaded += value.byteLength;

  // Chrome 157+: the standard shape, constructible in a service worker
  const event = new ProgressEvent("progress", {
    lengthComputable: total > 0,
    loaded,
    total,
  });

  client.postMessage({
    lengthComputable: event.lengthComputable,
    loaded: event.loaded,
    total: event.total,
  });
}

lengthComputable is set from whether the server actually sent a content-length, not hard-coded to true — that is the attribute’s entire job, and a relay that always claims a known length is lying about the one field it exists to communicate.

why the event rather than a plain object

The values are identical either way; an Event cannot cross postMessage, so both paths end up sending a plain object. What changes is where the contract lives. With ProgressEvent in scope, the field names, their types, and the meaning of lengthComputable come from the XMLHttpRequest Standard. Without it, every worker invents its own shape and every page has to learn that worker’s private convention — which is exactly the situation this feature ends.

It also composes with the Chrome 136 change that made loaded and total IDL double: a relay that forwards platform-constructed values inherits that precision instead of re-deriving it. See ProgressEvent loaded/total as double.

see also