Skip to content

Attempting to modify a fetch after it has completed / aborted / failed #448

Closed
@stuartpb

Description

@stuartpb

Documenting a side-conversation with @jakearchibald via Twitter DM spun off from #447:

Whatever approach we go with for aborting a fetch, there needs to be the question of what should happen when code tries to abort a fetch that has already finished, in one manner or another (completed, timed out, server hung up, already aborted by the exact same code, whatever).

(Note that I'm using the term "a fetch" to refer to an in-progress request dispatched by a caller of fetch(), instead of "a request", as Request right now can describe the object that a Service Worker receives as part of the fetch event that describes an inert request that has yet to be made, and I am taking pains to make the distinction.)

You could make the case that this is harmless, especially in a design where aborted fetches don't resolve/reject in the same way as a normal request/response (the way some of the Canceling proposals worked, for instance, especially the ones with a notion of "signaling disinterest and unsubscribing from the response").

However, my stance is that cancelling a request should be a synchronous operation that throws an error if the request has already terminated.


Note that I have since changed my mind about this

Skip to my next comment below, posted a few weeks later. It describes why going forward with the design described by the rest of this comment would be a bad idea.


It should be synchronous (not that I've really seen any contention around this, but just to be clear) because, as soon as the request has been aborted, there's no more room for any events in its lifecycle (not counting events around the lifecycle, such as rejecting any Promises etc waiting on the fetch, which should of course be triggered async as normal). Even if there were any I/O relating to the request after we've aborted it, it would no longer be considered part of the fetch. Once it's aborted, the fetch is closed - there's no in-between. A fetch has either finished or it hasn't, and once it's finished, it can't finish again.

It's my stance that attempting to abort a fetch that's already closed should throw an error (even if aborting doesn't cause the fetch to terminate, which, for what it's worth, I think would be wrong). This is the case I laid out to Jake:

  • It's really easy to avoid aborting a fetch that's already finished: whatever has the .abort() method (Request, FetchController, whatever) ought to have a .closed (or .finished or whatever) right next to it that, if your fetch-aborting code may potentially act on an already-finished fetch, it can check for (ie if (!rq.closed) rq.abort() or if (!rq.status.finished) rq.control.stop() or whatever).

  • It's not hard to imagine an edge case where this should be handled but could be overlooked. If a developer mistakenly attempts to abort a finished request, they get an error they can easily modify the code to avert: if a developer mistakenly aborts a resolved fetch that should have been handled differently and it doesn't throw an error, they'll get no indication of what's going wrong, short of hopping in the debugger and fiddling with breakpoints for an hour.

"Double resolution" errors

Say a Service Worker could be written for an endpoint that takes a long time to find invalid records, and has a quota for number of invalid requests that can be initiated. Under the assumption that all requests for resources it discovers are invalid will still be in progress when it aborts them, the service worker decrements the "Failed request" quota every time it aborts a fetch. The SW also handles fetches that come back as errors by decrementing the quota. If the endpoint speeds up one day, and all the requests that were previously being aborted before finishing are now being aborted after, with silent aborts, the quota would be depleted at double the normal rate, with no indication as to why. If trying to abort a request that's already finished throws an error, this pops the applicable error, and potentially averts whatever wrong behavior would have ensued after the double-abort.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions