#1089: Extended lifetime shared workers

Visit on Github.

Opened May 9, 2025

こんにちは TAG-さん!

I'm requesting an early TAG design review of extended lifetime shared workers.

We propose adding a new option to the SharedWorker constructor that serves as a request to extend its lifetime after all current clients have unloaded:

const sharedWorker = new SharedWorker(url, { extendedLifetime: true });

The primary use case here is to allow pages to perform some async work that requires JavaScript after a page unloads, without needing to rely on a service worker.

Discussions

Log in to see TAG-private discussions.

Discussed May 12, 2025 (See Github)
<!-- PRs -->
Discussed May 19, 2025 (See Github)

Xiaocheng: Shared workers are bound to pages. If you close all the pages, worker might be terminated. But some pages want to do async work after the page closes. They can resort to Service Worker. This proposal adds a flag to give the shared worker a longer lifetime. Don't think there are privacy/security considerations. API design. Thoughts are that first, the behavior is quite reasonable. Using Service Worker for async work is not great. Service Worker shouldn't be doing heavy work. But, this API is unnecessary because existing Shared Worker spec allows UAs to extend lifetime to a certain extent. Don't need this. Implementers can just do this. Don't see necessity. Unless there are cases where lifetime extension is undesirable in some other cases, so we need this to explicitly switch it on, but I don't see such cases. Suggestion is to leave a comment and ask about the use cases that make this necessary.

Marcos: Things like this come up a lot, where you have a use case that requires an extended lifetime. That use case should be the browser's responsibility without signaling. From a privacy perspective, once you shut the page, users expect the page to be dead, but this might be making network requests. Concerned that this approaches the problem the wrong way. "Extend this to perform actions that might not require an extended lifetime." It's a footgun that uses up system resources. If a thing requires to run after page unloads, this should be bound to page unloads. Use case is "page unloads, and then ???" It doesn't require a service worker but kinda requires service worker.

Xiaocheng: Navigation between two pages. Want Javascript during navigation. Ensure that some state is maintained during navigation.

Marcos: It's origin-bound anyway. If you're navigating ... don't want a shared worker across multiple origins. Act of navigating is the thing that would keep it alive. Some events already extend lifetime. E.g. in service worker, you have extendible events.

Jeffrey: respondWith()

Jeffrey: You might be agreeing with Xiaocheng, that in a same-origin navigation, the UA should keep the shared worker alive, while in a cross-origin navigation, maybe the UA should kill it quickly.

Marcos: Surprising that other solutions weren't proposed. But maybe they're explained in the explainer itself. E.g. the case in the motivation: fetch analytics (delayed beacon) would be covered by the API for fetchLater(). I get the use case, but I don't think it's done on the right level.

Jeffrey: I think Xiaocheng's question is good, and he should send it and see what Domenic says.

Marcos: Yes, just worried that there is a privacy concern. Don't need to mention that in the comment. I agree with the rest of the conclusion.

Xiaocheng: Will draft and send a comment.

Discussed May 26, 2025 (See Github)

Xiaocheng: We discussed it before but we missed its major use case which is to allow doing async work after the page is closed. Current approach is after page closed, a task is posted to a service worker, but this isn't great because a service worker is very heavy.

... The proposal is about allowing posting tasks to a shared worker. I wrote a draft comment in the private thread. After thinking about the major usecase, I still think it's a footgun because a developer might always set 'extent lifetime' to true - because there's no harm; it won't break their app, but adds possibilities for them. They may do this without realizing that it's increasing resource burden on users.

Martin: Have you looked at the WebKit standards position?

https://github.com/WebKit/standards-positions/issues/492

Xiaocheng: Not yet.

Martin: They were pretty negative on this one. It's probably timed out.

DanA: I share your concern Xiaocheng. Also concerned about user expectation. User has tab open; it's doing something; user decides 'I don't want this thing'; they click the 'close' control; but surprise - it keeps going. The undesired task continues.

... Yes, that may already be happening with a service worker, but maybe because they're more heavyweight there's less of a worry that they'll be overused.

Martin: Service workers have the same lifecycle that Xiaocheng is talking about here. They can be kept alive by mechansims such as promises, handling actions that they've been tasked with. There are no hard limits on them (or hard limits are on the order of 10 seconds, but these are not guarantees).

DanA: Could we post something that basically reflects the concern that Anne put forward in the WebKit position and saying that TAG shares this concern (not just following it). Need to make it clear this is also our own view.

Martin: Effectively the feedback that Xiaocheng already has, but add a paragraph that it removes the accountability feature that the web has which is that if you close a tab, it can't continue to do stuff indefinitely.

DanA: Do we want to close this, or suggest mitigations?

Resolution: Xiaocheng to post draft comment and include a mention of Anne's feedback.

Discussed Jun 2, 2025 (See Github)

Marked as pending now that the comment is posted. Skipped.

<!-- PRs -->
Discussed Jun 9, 2025 (See Github)

Max: They added a response to the explainer: https://gist.github.com/domenic/c5bd38339f33b49120ae11b3b4af5b9b#doing-this-automatically-or-with-an-opt-in-from-within-the-worker

Jeffrey: Interesting reply. They (Chrome + Mozilla) are saying it's about as expensive to extend the lifetime by one task as to extend by ~30s. Don't know the implementation well enough to challenge that.

Max: They also point to Safari feedback.

Jeffrey: Which looks negative from Anne.

Jeffrey: Privacy difference from my suggestion is negligible since the developer can always intentionally extend up to the UA's limit.

Jeffrey: I'm inclined to see what Xiaocheng thinks. He and Dan C have the most context.

Max: Their S&P Questionnaire says they plan to add Connsiderations to the HTML spec.

Jeffrey: And it's just an early review.

Jeffrey: I note that the explainer, with the alternatives considered, ought to land somewhere. A Gist isn't a long-term home.

Jeffrey: I'll work with Xiaocheng to post a comment.

Discussed Jun 16, 2025 (See Github)

Xiaocheng: Maybe we could add a new worker type.

Martin: Could work.

Jeffrey: Worried about a new worker type. Would be dedicated, but live longer than the page.

Martin: Cross of two types. Would have some properties like SW, but simpler.

Jeffrey: Recaptulates proposed feedback. They did suggest a worklet.

Xiaocheng: Worklets live in the page context, not quite the same.

Martin: Audio worklets do not.

Jeffrey: Worklets don't have an event loop, which this needs.

Martin: Xiaocheng's feedback seems right.

Jeffrey: Good to send.

Comment by @xiaochengh Jun 19, 2025 (See Github)
<details> <summary>repost by mistake</summary> Hi @domenic, thanks for pointing out the hidden resource cost for preparing a shared worker to have an extended lifetime. There is a concern we previously raised that isn't addressed yet:

What if developers set extendedLifetime: true to every shared worker just to be "safe" (as it won't break any app) without realizing the hidden resource cost of doing so?

Since using a shared worker causes so many detailed issues (like mismatching options, reconnection, etc), should we instead introduce a new type of worker (not a worklet as already discussed in the explainer) that is designed for this use case?

</details>
Comment by @jyasskin Jun 19, 2025 (See Github)

@xiaochengh, note that you already posted that comment, and Domenic replied to it.

Discussed Jun 23, 2025 (See Github)

Xiaocheng: Last time we raised the concern that dev would set the flag on all SWs to be safe. They replied that we don't see that behavior from fetch(keepAlive). It is hypothetical. Second concern is to propose a new worker type. Pushback is that introducing a new worker type is a lot of work. I don't know what to do. Presented with a bunch of bad solutions, and compare what's least bad.

Martin: Sympathize. Worker scope seems like a lot of work, but commensurate to the cost to end users? Maybe spec writer cost should match the cost to users. If pages can run things in the background after page close, that's a big ask.

Xiaocheng: How strong is this point? I'm leaning to closing as satisfied-with-concerns.

Martin: Do we have a more passive-aggressive one? "ambivalent"?

Jeffrey: I'd be fine with satisfied-with-concerns. [other discussion]

Martin: ambivalent. If we think workers are the right thing, it should be cheaper to create a new kind of worker. New kinds of worklets are easy. New kinds of workers should be too.

Xiaocheng: It's between worklet and worker, although they gave arguments against worklets. API design is hiding the cost of doing this.

Martin: Is there a version of this that works without creating a SharedWorker in a particular mode? e.g. if the page closes and it has outstanding work, through whatever system indicates to the browser that the outstanding work exists, it can keep the worker alive?

Jeffrey: That's what I suggested, and they said that's hard to implement. I don't totally buy it, but I also don't know the implementation well enough to argue this.

Martin: When you register 'pagehide' or "I'm going to run a cleanup task", you've registered an intention to clean up after yourself on the other end. So have registering those events enable extended-lifetime on all future shared workers, or on a particular shared worker url. Register something with the browser that you want to do a cleanup. That action associates state with the new shared worker. Can mutate that over the page's lifetime. Then when the page ends, that state is passed to the shared worker.

Jeffrey: Kinda like that design better. Maybe we should suggest they consider it as an alternative.

Martin: [more discussion]

Jeffrey: Ah, the "I'm going to pass state" flag might turn out to be the same as the proposed "extended lifetime" flag.

Martin + Xiaocheng to continue iterating.