-
Notifications
You must be signed in to change notification settings - Fork 296
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
Abort controller design #438
Comments
AbortSignal gains an associated incoming port (a MessagePort object) and outgoing ports (a set of MessagePort objects). AbortSignal object serialization steps:
AbortSignal object deserialization steps:
I think this works, but it needs to be double checked. cc @domenic |
Overall seems pretty reasonable standalone. I think it'd help to see this in action though by defining the transfer steps for Request that use it.
I wouldn't; it's useful to sometimes pass things around without having to do a state-check and a separate code path.
Yeah... this and the event listener on the other side really cry out for a better spec-level abstraction for these things. Maybe the port message queue is what should be used here? It's fine for now though.
Second arg should be a list. |
I'm not sure I follow, currently a |
Where I invoke StructuredDeserializeWithTransfer I should also get the [[Deserialized]] slot from the return value. |
I think I may have said that in the past, but I was confused; streams use these only for piping operations, but don't store signals themselves internally. So if you transfer a stream there's no AbortSignal that would go along with it whose transferring/cloning we would need to define.
Not via structured serialize... but we do something very similar, and hacky, in the fetch spec now, to get it from the service worker to the main thread. (Both Request and Response, I guess.) And the idea would be that, if we send a Request to a service worker, it needs to bring along its AbortSignal, so the service worker can know about any abort requests. I think the current hacky serialization should ideally be based on structured serialize? Not as sure about that... |
Currently we just pass the underlying request/response across threads, which is somewhat similar to passing JavaScript records across, although streams made that less ideal. I guess we should flush this out more when we start working on defining abort in Fetch in much more detail. |
New APIs for a generic reusable abort mechanism. Tests: ... Fixes part of whatwg#438. (This commit was also authored by Jake and Anne.)
Right. To be clear the steps around 10.3.1.2.5 in https://w3c.github.io/ServiceWorker/#fetch-event-respondwith are what I'm referring to. They are very similar to what transferring a stream will eventually look like (at least for byte streams), and so we could probably shorten them greatly by factoring out "transfer a stream". And then probably "transfer a stream" will also be used to allow structured transfer of the stream. However I realized that in order to make fetch() abortable we're going to need to store the abort signal in the stream after all, if we want the abort signal to control both the initial headers request and the body. So we will need to define transferring/cloning of AbortSignals in order to define transferring of streams. |
This won't include fetches that are terminated as part of terminating the fetch group right? They're terminated with "fatal". Feels like the service worker should hear about this, so the developer can abort related fetches. |
I wonder if we can do something to aid extensibility here, so a subclass could do something like: A FetchSignal has an associated priority, which is a number. It is serializable. Then, to update the priority of a fetch signal to newPriority:
Some rough scribblings around how this could work: Then AbortSignal's serialize steps could be:
To deserialize AbortSignal:
To broadcast to cloned listeners a given algorithm name and array of arguments:
|
It's kind of unprecedented to automatically make subclasses serializable, but I agree we want to find some way to make it easy. |
Do we need to solve that for v1? |
I think v1 can be same-thread, so no serialisation needed. |
New APIs for a generic reusable abort mechanism. Tests: ... Fixes part of whatwg#438. (This commit was also authored by Jake and Anne.)
New APIs for a generic reusable abort mechanism. Both Fetch and Streams will build on this initially. Tests: web-platform-tests/wpt#5960. Fixes part of whatwg#438. (This commit is also authored by Jake and Anne.)
New APIs for a generic reusable abort mechanism. Both Fetch and Streams will build new features on this. Tests: web-platform-tests/wpt#5960. Fixes part of #438. (This commit is also authored by Jake and Anne.)
signal seems have a generic role to send SIGNAL not only so, |
I think we can go from |
how about Controller ? |
Same |
basically signal will came from controller, so lots of case we need not to care about class of Signal. const controller = new AbortController(); // explicit
const signal = controller.signal; // inplicit
controller.abort() // only for abort usage
controller = new TaskController();
signal = controller.signal
signal.addEventListener('any', ...)
controller.dispatchSignal('any', ...) 'abort' ing task is one use-case of 'abort' instead of 'any'. |
@Jxck all the use cases we have thus far are either abort or abort + something else. If at some point we need something that is not abort, but does fit the same kind of pattern, we can generalize quite easily and have SignalController and Signal or some such and just make AbortController and AbortSignal inherit from those. |
ok thanks. one more thing. |
@Jxck web-platform-tests/wpt#6484 (comment) may help. Developers wanted to separate the ability to abort from the ability to react to a signal to abort. It also simplifies the design & avoids race conditions. Imagine a signal that can also change priority. If you've posted that to a worker, you now have two threads that can change the priority. |
thanks @jakearchibald . I found signal can use multiple. const controller = new AbortController()
const signal = controller.signal
Promise.race([
fetch(url, {signal}),
fetch(mirror1, {signal}),
fetch(mirror2, {signal}),
fetch(mirror3, {signal}),
]).then((res) => {
// first fetched response
console.log(res)
// stop rest of fetch
// abort after resolve race(), so AbortErrors are unhandled
controller.abort()
}).catch((err) => {
if (err.name == 'AbortError') {
// abort before race hasn't resolved
return
}
// fail to race
console.error(err)
}) |
@Jxck I'm not sure what does exactly that you think. Also, note that you're also aborting |
I meant we can stop rest of task when first task finished and Promise.race() resolved. |
I'm going to close this since |
Why not made a "FetchController" or "RequestController" - And so allow to handle "progress"-events too? And does it really makes sense not to use the controller directly, but an additional "signal object"?
I'm not deep in the subject, so you can answer briefly. |
@nuxodin the plan is to have a fetch observer for this: fetch(url, {
observe(observer) {
// observer will fire events & have properties related to the fetch.
}
}); |
#437 is the latest instance of generic design of the abort controller. (#434 is a previous iteration that also has discussion.)
whatwg/fetch#523 is a specific implementation of the abort controller for the Fetch API.
We need to decide if the generic abort signal needs to be serializable. @domenic argued in favor. Such a design would require a private MessageChannel. We might have to abstract MessageChannel or create some technical debt for someone to cleanup later.
For the Fetch API we will need to expose a signal to the service worker that signals "abort" whenever the "main thread" signal signals "abort" or the "main thread" fetch that caused the service worker to dispatch a fetch event is terminated with reason "abort" (I don't think we want to trigger that signal in the service worker for other reasons).
I believe these are the main outstanding things to sort through. Apologies to everyone about the various repositories this ends up involving. Open to suggestions on how to organize things better.
The text was updated successfully, but these errors were encountered: