-
Notifications
You must be signed in to change notification settings - Fork 312
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
async waitUntil() does not allow extension on final promise resolution? #1039
Comments
FWIW, we have implemented this behavior in FF and written a WPT test verifying it. See: And: |
Yes, that's right. I think we should add all the derived promises called by |
Related(?):
where |
This is not related to the issue posed in the OP. I'm surprised it didn't work. Both Blink and Gecko implemented it. On which environment did you tried it?
The
|
@jungkees thanks for the info - and sorry for posting stuff that is not related to this issue. Let me know if I should move this discussion somewhere else. To me this seems under specified. Since my use case is with the PushEvent I looked into that spec, and "waitUntil" is not mentioned there. |
Did you use a code pattern as described in the OP? Like: evt.waitUntil(p);
// Code can be defined to run on the final keep alive promise resolution that
// then keeps the SW alive again.
p.then(_ => {
// I think the spec says this should throw, but I thought we agreed this should work.
evt.waitUntil(fetch(evt.request));
}); Or the promise returned from |
(1) This changes the approach to unsetting the extendable events' extensions allowed flag. This change introduced a reference count based approach instead of the promise-copying-and-checking approach. (2) This also extends the opportunities of the lifetime extension by allowing calling waitUntil() within microtasks queued by the given promise's Promise.prototype.then callback. Related issues: - #931 (1) - #935 (2) - #1039 (2)
I'm working on this in #1049, and had a feedback that if this design is tight: #1049 (comment). Also I sort of touched the ECMAScript internal slots to spec this, and that seems hacky. So, I'd like to understand more of how implementations actually did and will do to find a right specification devices and methods. /cc @wanderview @mattto @domenic @annevk @jakearchibald @slightlyoff |
I think there might be confusion here, looking at some of the examples. Given the following: e.waitUntil(p);
const q = p.then(() => {
e.waitUntil(r);
}); My impression is that the OP wants to wait until However, the implementation in #1049 is doing something completely different: it's waiting on This can be further exemplified by changing the above example a bit: e.waitUntil(p);
const q = p.then(() => {
e.waitUntil(r);
return new Promise(() => {}); // never settles, thus q never settles
}); The implementation in #1049, since it attempts to wait on |
I believe you meant p and r here. I think this design is reasonable.
Right. I added q to the pending list, and that's why I ended up poking the internal slots. I thought we should guarantee all the then callbacks in the given promise's promise chain. E.g. e.waitUntil(p);
p.then(doSomething).then(() => { e.waitUntil(r); }); Now the more I think about it, the more convinced that I have misunderstood the original issue. |
Thanks, edited! |
(1) This changes the approach to unsetting the extendable events' extensions allowed flag. This change introduced a reference count based approach instead of the promise-copying-and-checking approach. (2) This also extends the opportunities of the lifetime extension by allowing calling waitUntil() within microtasks queued by the given promise's Promise.prototype.then callback. Related issues: - #931 (1) - #935 (2) - #1039 (2)
#1050 is related, I think |
…#1049) (1) This removes the extendable event's extensions allowed flag and introduces a reference count based approach instead of the promise-copying-and-checking approach. (2) This extends the opportunities of the lifetime extension by allowing waitUntil() in the task queued by the reaction to the given promise. Fixes #931 #935 #1039 #1050.
Closed by 6c1f3fe. |
Whaat, how did this stuff start to use tasks. That makes it very random how long one can still add waitUntil promises to the event object. There can be many other pending tasks already and all those might get run before the queued task. |
To adjust my original example code I think we agreed to this:
So I think queuing a full up task is wrong. |
e.waitUntil(p) // p.then (i.e., upon settlement step in the spec)
p.then(_ => e.waitUntil(q));
p.then(_ => e.waitUntil(r)); Should the I think we should either use a normal task as spec'd or consider the design in #1049 (comment). |
I don't think either q or r waitUntil calls in your example shoudl throw. However, s here should throw:
|
That behavior means, The "count decrease" job should be queued as a microtask exactly in the position marked $ below: (assuming "||" is a task boundary, "[]" is a microtask.) even though p.[[PromiseFulfillReactions]] contains the reactions in this order: ["count decrease", q, r, the chain for s]. Also it seems we'll need some customization to Promise hooks (https://html.spec.whatwg.org/#enqueuejob(queuename,-job,-arguments) or somewhere) to record and detect the depth of the then callbacks for the queued microtasks? |
We should not add new hooks just for this feature. Either you throw when you try to add q, you queue a microtask upon fulfillment and decrease from that microtask, or you go with the task-based option. |
I think this would satisfy Ben's mental model. This would even work for all sequences: e.waitUntil(p);
p.then(_ => e.waitUntil(q));
p.then(_ => e.waitUntil(r));
p.then(_ => e.waitUntil(q));
e.waitUntil(p);
p.then(_ => e.waitUntil(r));
p.then(_ => e.waitUntil(q));
p.then(_ => e.waitUntil(r));
e.waitUntil(p); I'm happy with this solution. |
@annevk, queuing a microtask on the same event loop using the same task source that I used for the normal task makes sense? |
@annevk, I mean queuing a microtask requires specifying a task source as well as an event loop like a normal task case? |
No, microtasks don't have a task source. If you do it as part of the fulfillment steps it should be fine. |
@wanderview does this need to be open, given the things covered in #1213? |
#1213 should take precedence now, but this issue might be useful for understanding how I helped mess up the WPT. |
Closed in favour of #1213 - it's still here for reference. |
I feel like we discussed this case at one point, but can't find where:
I don't think the spec allows this as currently written. Step 4 here:
https://w3c.github.io/ServiceWorker/#extend-service-worker-lifetime-algorithm
Unsets the flag allowing extension immediately after the current set of extension promises settles in step 2. I think this blocks the addition of new extension promises?
Or is there something which guarantees other .then() handlers will run between step 2 and 3 here?
I think we should either explicitly have a microtask to run step 3/4 or run add a comment about some hidden invariant which results in this outcome.
The text was updated successfully, but these errors were encountered: