-
Notifications
You must be signed in to change notification settings - Fork 316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ready promise is kind of weird with overlapping scopes #1278
Comments
Also, it feels wrong to me that the spec kind of makes the result here depend on when the ready promise is accessed. It feels like the spec should automatically resolve the ready promises of any matching clients whether the promise has been accessed or not. That would provide the most predictable behavior. |
Maybe waiting to do any logic until the first ready promise access is intended to help avoid weirdness like bug #1279, but it doesn't seem particularly effective and leads to more complications. |
If I understand correctly, both accesses have to resolve to the same result, as they return the same promise (similar to my comment on #1279) |
Sure, but which result? There are two possibilities AFAICT:
I think (1) is objectively better, but the spec kind of says (2) right now. I think these steps need to set some kind of flag on the context object indicating "ready promise is being processed" and then make step 1 early abort if that is set: https://w3c.github.io/ServiceWorker/#navigator-service-worker-ready Making step 1 wait for settled creates the potential for two sets of parallel steps that race each other. |
Oh, I also thought about this a bit more. I do actually like waiting for the first access to start this process. I don't really want to send ready promise updates out to a bunch of clients that aren't even looking at them. Edit: Also, this change wouldn't get rid of the issue where "when you check determines what you get". It just moves the "when you checked" time from |
Ah, I see what you mean. Chrome does (1). Once we start the "ready" process, any subsequent access just gets added as a result callback to the process. After the ready result is known, any access gets back that result immediately. It does look like the spec needs a kind of "ready promise is being processed" flag to describe that. |
I happen to have been working on #1277 from a day before this issue was filed. It seems (1) still doesn't solve the multiple registrations case. Would it be impossible at all to make |
I think its discouraged to change a property that returns a promise. It would be more conventional if it was a function. The other part of the spec that I don't think browsers implement is the bit about locking in the registration and waiting for it to activate in step 3 here: https://w3c.github.io/ServiceWorker/#navigator-service-worker-ready The implication of the current spec text is that you can get a registration with a I don't think browsers do this. I think at least firefox and chrome probably wait for the first registration that gets a |
I don't know the context here very well, but it's OK to change a property that returns a promise, if there's some kind of conceptual "reset" that makes the promise represent a new thing. For example, we use that pattern in streams: https://streams.spec.whatwg.org/#default-writer-ready |
The guidance from @annevk (and seemed also from @domenic) on #223 seemed to be that the promise shouldn't change: "always returning a new object from an attribute is a non-starter". Also see the comment on blink-dev https://groups.google.com/a/chromium.org/d/msg/blink-dev/jjh4KUS0cS0/c81n5M6c8qwJ There might be confusion about what's a "stable promise". I think there's two possible definitions of stability: 1) whether the property can vend multiple promises:
And 2), whether the promise can resolve/reject to different values over its lifetime.
@yutakahirano tells me stability 2) is always the case: once a given promise resolves/rejects, it always resolves/rejects to the same value. Is the stability 1) assumption no longer always true? |
There's a difference between "always returning a new object" and "when some conceptual reset happens, returning a new object". The former breaks |
I see. From an implementer POV it's easiest to just break .ready === .ready and have each access get a new Promise. If we try to keep the same promise, except when there was a change, it seems likely to cause races: We have to cache .ready in each context and then alert each context when there was a change (unregister or a new register took effect), and that alert might or might not reach the context before .ready resolves again. If we can't break .ready === .ready, I'm kind of inclined to just keep the existing weird behavior where .ready can resolve to the non-active registration. |
Please don't break it, such behavior is very confusing. |
If we don't want to change it, developers would have to do something like: let reg = await navigator.serviceWorker.ready;
const latestReg = await navigator.serviceWorker.getRegistration();
if (reg !== latestReg) {
await waitForState(latestReg.installing, 'activating');
reg = latestReg;
} Or should we provide a method (e.g. |
I think the returned ready promise would be resolved by some later access to |
Sure. I'd like to hear more from implementers about feasibility. Here's my thought on @mattto's comments.
I think we're already maintaining .ready for each context. Yes, alerting each context from unregister and Activate would have to be added. We're already doing similar signaling for
AFAICT, the matching service worker registration calls initiated from |
Well, there is a slight difference in we only have to signal statechange and controllerchange events where a From an implementation perspective its nice to be able to lazy create the |
I agree we should avoid looking at potentially every client. Would it be possible to confine signaling activation to those clients that already lazy-created the |
Right and it's a bit tricky in our implementation already to send the signals in the right order. For example we have to send the "change to active" message before the "resolve ready promise" message, to ensure inside the ready.then() that registration.active is populated. It'll be more complex to ensure .ready resolves at the right time when the registration changes (due to unregistration or new registartion). It's all doable but it could be tricky to get it right, and there could be some unavoidable races. |
This matches what I plan to do in the implementation. The tricky bit is that you also have to handle cleaning up the promises if they never settle and the client is detached/destroyed. I have an implementation way to do that, but not sure about spec language. |
Honouring semantics, I would say it should start waiting on Another alternative is to reject a second |
Please note that the second |
Can not these clients keep track of their |
F2F: We discussed some options:
IMO: We should just add .getReadyPromise() [with a pithier name] and leave .ready semantics as they are instead of trying to improve it while stuck as a property. And if there are no actual problems this is causing in the wild, just don't change anything for now. |
Consider this situation:
The first ready promise gets the '/bar' scope registration and waits for it to activate. This is step 4.3 of here:
https://w3c.github.io/ServiceWorker/#navigator-service-worker-ready
Assuming that the '/bar' worker does not activate immediately, we might get the '/bar/baz' worker registration started.
What should the second
.ready
promise access do? The promise is not settled yet, but the previous access is in a parallel bit of algorithm waiting for '/bar' to activate. Should the second access block on that somehow or start waiting on '/bar/baz' instead?Kind of a corner case, but its a bit weird.
The text was updated successfully, but these errors were encountered: