-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Support abort API #592
Support abort API #592
Changes from 23 commits
93e9cec
53565c1
9a7d232
fd5c261
318e95f
0e1b651
d47f6c9
529c9fa
8ba0cdb
074c89e
b832229
c6aff30
d9aa590
886a076
9f9a00f
bab0f3d
0bed2ba
66ed8ba
738d51a
8f778f3
f0e24f8
36bf692
7f6b8d2
c31fca9
f035860
2bd7f22
c873843
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -317,6 +317,7 @@ | |
} | ||
this.method = input.method | ||
this.mode = input.mode | ||
this.signal = input.signal | ||
if (!body && input._bodyInit != null) { | ||
body = input._bodyInit | ||
input.bodyUsed = true | ||
|
@@ -331,6 +332,7 @@ | |
} | ||
this.method = normalizeMethod(options.method || this.method || 'GET') | ||
this.mode = options.mode || this.mode || null | ||
this.signal = options.signal || this.signal | ||
this.referrer = null | ||
|
||
if ((this.method === 'GET' || this.method === 'HEAD') && body) { | ||
|
@@ -415,15 +417,38 @@ | |
return new Response(null, {status: status, headers: {location: url}}) | ||
} | ||
|
||
var DOMException = self.DOMException | ||
try { | ||
new DOMException() | ||
} catch(err) { | ||
DOMException = function(message, name) { | ||
this.message = message | ||
this.name = name | ||
var error = Error(message) | ||
this.stack = error.stack | ||
} | ||
DOMException.prototype = Object.create(Error.prototype) | ||
DOMException.prototype.constructor = DOMException | ||
} | ||
|
||
self.Headers = Headers | ||
self.Request = Request | ||
self.Response = Response | ||
|
||
self.fetch = function(input, init) { | ||
return new Promise(function(resolve, reject) { | ||
var request = new Request(input, init) | ||
|
||
if (request.signal && request.signal.aborted) { | ||
return reject(new DOMException('Aborted', 'AbortError')) | ||
} | ||
|
||
var xhr = new XMLHttpRequest() | ||
|
||
function abortXhr() { | ||
xhr.abort() | ||
} | ||
|
||
xhr.onload = function() { | ||
var options = { | ||
status: xhr.status, | ||
|
@@ -443,6 +468,10 @@ | |
reject(new TypeError('Network request failed')) | ||
} | ||
|
||
xhr.onabort = function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need to attach There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mislav I believe this is the most correct as mentioned here. Calling abort on the XHR will set the right status code and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see.. xhr is never exposed so it can't be aborted from the outside. Is there any other way it could be aborted (e.g. by the browser)? If not, there shouldn't be any difference. I don't mind this way though, reads well :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dysonpro That's correct, but we aren't interested in whether There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @mislav for your explanation. I am excited about this functionality so I have been following this PR carefully. I am already using this branch code in a project that requires aborting no longer needed requests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dysonpro Your concerns are valid, but we have different priorities when thinking about this. At the point when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
These are rather obscure use cases, but they're real nonetheless. I think a general There's nothing wrong with immediately rejecting after calling |
||
reject(new DOMException('Aborted', 'AbortError')) | ||
} | ||
|
||
xhr.open(request.method, request.url, true) | ||
|
||
if (request.credentials === 'include') { | ||
|
@@ -459,6 +488,17 @@ | |
xhr.setRequestHeader(name, value) | ||
}) | ||
|
||
if (request.signal) { | ||
request.signal.addEventListener('abort', abortXhr) | ||
|
||
xhr.onreadystatechange = function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now attach both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is the cleanest/safest way to know when the request has finished, regardless of the result, instead of having to do so on every callback: xhr.onload = function() {
...
request.signal.removeEventListener('abort', abortXhr)
}
xhr.onabort = function() {
...
request.signal.removeEventListener('abort', abortXhr)
}
xhr.onerror = function() {
...
request.signal.removeEventListener('abort', abortXhr)
}
xhr.onprogress = function() {
...
// should I remove listener? no.
}
// what other events there are? timeout?
... having the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another possibility is With |
||
// DONE (success or failure) | ||
if (xhr.readyState === 4) { | ||
request.signal.removeEventListener('abort', abortXhr) | ||
} | ||
} | ||
} | ||
|
||
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) | ||
}) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ | |
</script> | ||
|
||
<script src="/node_modules/promise-polyfill/promise.js"></script> | ||
<script src="/node_modules/abortcontroller-polyfill/dist/abortcontroller.js"></script> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can now import |
||
<script src="/test/test.js"></script> | ||
<script src="/fetch.js"></script> | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there's a polyfill that we audited and recommend using, then we should link to it here.