v153 · performance · workers

Long Animation Frames, inside a worker

Long Animation Frames report the main thread's slow frames with enough attribution to name the script that caused them. Workers have no frames, so they were left out — and a worker that blocks its own event loop for half a second is, today, completely invisible to the page that is waiting on it.

concepts

  1. What each thread can observe

    The supported entry types on the main thread and inside a real worker, side by side. The gap between the two lists is the feature, and it is a short list on one side.

  2. Blocking a worker on purpose

    Jam a worker's event loop for as long as you like and watch who notices: the main thread's observer, the worker's own observer, and a round-trip ping that simply never comes back.

  3. The workaround, and what it cannot tell you

    You can time a worker's tasks yourself with a self-rescheduling timer. Compare what that gives you against a real Long Animation Frame entry from the main thread — the duration matches, and everything useful is missing.

why it shipped

The main thread has had a good answer to "why was that slow?" since Long Animation Frames landed: an entry per slow frame, with the rendering, style and layout time broken out and a list of scripts with their source URLs and character positions. Workers, being off the rendering path, got none of it — longtask was never exposed there either.

That leaves a real blind spot. Work is moved into a worker precisely because it is heavy, and heavy work is what blocks event loops. When a worker stalls, the page sees a message that does not arrive, with no way to find out what the worker was doing. Extending the API to workers reports a blocked worker event loop as a long-animation-frame entry inside that worker, with the same script attribution.

the API

// Inside the worker — the same shape as on the main thread.
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.duration, entry.scripts.map((s) => s.sourceURL));
  }
}).observe({ type: "long-animation-frame", buffered: true });

enabling it now

Not available on the Chrome 150 used to build these pages, with or without a flag. Measured directly: inside a worker, PerformanceObserver.supportedEntryTypes is ["mark", "measure", "resource"] both with and without --enable-experimental-web-platform-features, while the main thread of the same browser lists long-animation-frame.

// main thread:  [… "long-animation-frame", "longtask", …]
// worker:       ["mark", "measure", "resource"]

So the demos here measure the blind spot rather than the fix. Each one reads the live list rather than a recorded one, and will report the feature the moment a browser has it.

references