Skip to content
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

[WIP] Add abort support #437

Closed
wants to merge 3 commits into from
Closed

[WIP] Add abort support #437

wants to merge 3 commits into from

Conversation

TimothyGu
Copy link
Collaborator

This is still a WIP:

  1. Tests are needed.
  2. Use a better AbortSignal type check after Make AbortSignal and AbortController stringify correctly mysticatea/abort-controller#5 is applied.

Note: this PR will introduce a runtime dependency on https://github.com/mysticatea/abort-controller.

Fixes: #95

@TimothyGu TimothyGu mentioned this pull request Mar 27, 2018
@codecov-io
Copy link

codecov-io commented Mar 27, 2018

Codecov Report

Merging #437 into master will decrease coverage by 4.77%.
The diff coverage is 45.83%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #437      +/-   ##
==========================================
- Coverage     100%   95.22%   -4.78%     
==========================================
  Files           6        6              
  Lines         503      544      +41     
  Branches      153      164      +11     
==========================================
+ Hits          503      518      +15     
- Misses          0       21      +21     
- Partials        0        5       +5
Impacted Files Coverage Δ
src/request.js 86.86% <35%> (-13.14%) ⬇️
src/index.js 88.77% <42.1%> (-11.23%) ⬇️
src/body.js 98.91% <77.77%> (-1.09%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 989c843...a823a1e. Read the comment docs.

if (signal.aborted) {
abortController.abort();
} else {
signal.addEventListener('abort', () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This eventListener is never removed, is it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it could be of any help maybe adding {once: true} to addEventListener could help cleaning up things

Copy link
Collaborator Author

@TimothyGu TimothyGu Apr 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mika-fischer No, and it's very difficult to do so. We could add a { once: true } though.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But won't this cause issues, for instance in the case where I have a long running Node.js application where I have a global AbortController (to be able to abort all requests when the application shuts down) and each request in the whole lifetime of the app (let's say hundreds of thousands) adds a listener, none of which is ever removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😔 yes. It’s also one of the reasons why this is a WIP.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you just save a cleanup closure (which just removes the listener from the parent AbortController) and attach this closure to the appropriate events of the Node.js Request/Response so that it gets called when the request is finished?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A cleanup closure would remove the listener from the parent AbortController after fetch() terminates for any reason, right? Would that affect the ability to cancel a fetch that reuses a Request object for multiple fetches? Does that maybe mean that the listener should be added each time the fetch begins and removed when the fetch terminates?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.once() seems like it would only help if the request terminated by being aborted, but I might be missing something.

@RikkiGibson
Copy link

Hi there. I would love to see this happen. Is there any way I can assist?

@TimothyGu
Copy link
Collaborator Author

Hi @RikkiGibson, yes! The biggest thing right now is making sure the "abort" signal handlers get cleaned up correctly, i.e., #437 (comment). But I'd appreciate any help in adding tests as well.

You could submit a pull request against the abort feature branch.

@@ -82,11 +100,23 @@ export default class Request {
}
}

const abortController = new AbortController();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could someone explain why we are initializing another internal AbortController?

Copy link
Collaborator Author

@TimothyGu TimothyGu Jun 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per spec, the AbortSignal we expose through signal must be a sanitized object, and not be the signal passed in. To create a new AbortSignal we have to create another AbortController.

@heri16
Copy link

heri16 commented Jun 18, 2018

I haven't been able to find the spec that require signal to be exposed. Where can I find this information?

@RikkiGibson
Copy link

https://fetch.spec.whatwg.org/#request-class

Look for the Request constructor. Something like step 30 it will mention that the passed in signal must be used to create a “following” signal, i.e. one that subscribes to a parent signal (given by the user) but can also be canceled independent of the parent signal.

@@ -39,17 +39,35 @@ export default function fetch(url, opts) {
return new fetch.Promise((resolve, reject) => {
// build request object
const request = new Request(url, opts);
const signal = getAbortSignal(request);
if (signal.aborted) {
reject(new FetchError(`Fetch to ${request.url} has been aborted`, 'aborted'));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any way this could be more inline with the browser spec? rejecting with an AbortError? when using with isomorphic fetch this would result in having to catch two types of errors for node and browser

https://dom.spec.whatwg.org/#aborting-ongoing-activities

@bitinn
Copy link
Collaborator

bitinn commented Nov 5, 2018

A shoutout to people watching this thread, we are closed to merging AbortSignal support, it's a bit different from previous WIP, so let us know if you have any feedback on it: #539

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Aborting a fetch
8 participants