You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AbortSignals are used in fetch to abort a request. They are useful to close requests if they don't happen to be necessary. AbortSignals are not widely used yet in the JavaScript ecosystem but they seem like they could be useful if they would find a wider adoption.
A node.js API that comes to mind is if a web framework would provide an abort signal that this could be comfortable to be used as a means to abort a file stream:
Which could be an easy means to stop allocating money if a CLI tool is used to read a very large file by accident.
At the current point I am not sure at how many places this would be useful, or how useful it would be at each case, but I think opening the discussion has merit.
I can see how a generalized cancellation mechanism would be useful; that's essentially what require('domain') tried to do (albeit with flaws.)
Probably the best way to get the conversation going is by opening a pull request that adds AbortSignal support in a few places, let's say net.connect(), http.get() and fs.createReadStream().
The exact set of methods doesn't really matter as long as it's diverse enough to get a feel for the necessary changes.
process.abortSignal - can you explain why you suggest a global AbortSignal? It doesn't seem useful, it's a one-shot thing, right?
There is has been work in TC39 to provide cancellation and cleanup protocols within the language, which is why I've avoided pushing too much on this. Using the web API might be fine, I just want to make sure we don't feel forced to pick that specific API.
I have been experimenting a bit recently with abort signals and the usefulness it could provide (it is pretty hard actually). A few interesting examples are listed below:
Probably the best way to get the conversation going is by opening a pull request that adds AbortSignal support in a few places, let's say net.connect(), http.get() and fs.createReadStream().
That sounds good, I'll start out with fs.createReadStream() as local file operations would seem the easiest place to work out initial problems, then work on the other two.
Reacted by Martin Heidegger and Benjamin Gruenbaum
I'd be in support of adding a cancellable primitive to some base operations such as readFile or writeFile and other callback-based APIs. I kindly ask to have it support both EventTarget and EventEmitter, as Node.js is EventEmitter based.
Is EventTarget needed at all?
I'd need to know more about adding it to streams and http.
Is extremely far from what anybody that serve files with Node.js core would do. Check out https://github.com/pillarjs/send to know more (at the time of this writing, it's 1129 lines of code).
The snipped above works only because req and res are connected and they would be destroyed at the same time. If we stream to another location, it will leak a file descriptor and memory:
Note that all the above is already solved in Node.js with pipeline(), which supports async generators and iterators as well. It's based on the standardization of the stream.destroy(err) operation which is effectively the "abort mechanism" for streams, so the groundwork for adding signals is already done.
There is also the question if we should not just add something like:
destroyOnAbort({ stream, signal })
To achieve our goal.
I would like to see some forward-thinking on how this API will be used by the Node and JS ecosystem and the interaction with the rest of the Node.js Stream libraries.
Would you mind to articulate a bit more on what type of use cases would you like to solve with these and do several examples?
It might well be that we should create a repo and go through some code example before settling into how this API would work.
@mcollina Just about the EventTarget/EventEmitter part: When you construct an AbortController, that will create an AbortSignal, which would be some kind of event thing. I was imagining that this would initially be an EventEmitter, but if we could make it also act as an EventTarget, then this could improve Node/Web compatibility: for the case where you write some code that's cancellable and want to listen for the cancellation being triggered, you'd use .on() or .addEventListener() depending on whether it's an EventEmitter or EventTarget.
Just about the EventTarget/EventEmitter part: When you construct an AbortController, that will create an AbortSignal, which would be some kind of event thing. I was imagining that this would initially be an EventEmitter, but if we could make it also act as an EventTarget, then this could improve Node/Web compatibility: for the case where you write some code that's cancellable and want to listen for the cancellation being triggered, you'd use .on() or .addEventListener() depending on whether it's an EventEmitter or EventTarget.
I'm generically ok with the above, I'd like to see an implementation of this and how it could be used in core.
(We might work on this in a fork of Node.js similarly to how quic and esm have been developed).
Reacted by Daniel Ehrenberg and Benjamin Gruenbaum
Just an update here that AbortController and Event are now live and mostly whatwg compatible (we're adding final tests and fixing things). Some Node.js APIs have already gotten AbortSignal support (like EventEmitters and promises versions of timers).
node_fetch previously had a timeout option but was deprecated/removed in v3 in favor of abortSignal + timer was not a spec'ed
A Wishful replacement would be a timer that can return a signal too - something like
A Wishful replacement would be a timer that can return a signal too - something like
Actually, having a helper on AbortControllers to abort automatically after a time could be interesting/useful though it's probably a good idea to ask that of whatwg (we can extend it and ship a subclass but would rather not until working with spec bodies hasn't worked - and so far whatwg have been very helpful when we presented compelling cases for APIs that would make sense in browsers as well to them).
We've landed a bunch of apis in fs/child_process/http/http2 with AbortSignal support since this btw, we also added signal support to addEventListener and are working on a way to chain AbortSignals correctly.
Ooh, cool I just realized both your original examples already work in Node now :] :
// Your example herecreateReadStream('file.bin',{signal: req.signal}).pipe(res)// Becomes (and is working today in NodenewReadable({ signal , ... rest}).pipe(res);// or, if you don't control creationaddAbortSignal(signal,getReadableSomehow()).pipe(res);// And your example hereawaitreadFile('file.bin',{signal: process.abortSignal});// just works as is with `fsPromises.readFile`
Activity
bnoordhuis commentedon Feb 27, 2020
I can see how a generalized cancellation mechanism would be useful; that's essentially what
require('domain')tried to do (albeit with flaws.)Probably the best way to get the conversation going is by opening a pull request that adds AbortSignal support in a few places, let's say
net.connect(),http.get()andfs.createReadStream().The exact set of methods doesn't really matter as long as it's diverse enough to get a feel for the necessary changes.
process.abortSignal- can you explain why you suggest a global AbortSignal? It doesn't seem useful, it's a one-shot thing, right?devsnek commentedon Feb 27, 2020
There is has been work in TC39 to provide cancellation and cleanup protocols within the language, which is why I've avoided pushing too much on this. Using the web API might be fine, I just want to make sure we don't feel forced to pick that specific API.
martinheidegger commentedon Mar 11, 2020
I have been experimenting a bit recently with abort signals and the usefulness it could provide (it is pretty hard actually). A few interesting examples are listed below:
In the app I also made use of a "home-made", process-abort-signal here:
https://github.com/consento-org/dat-ssg/blob/6bc7007fe6d109f3535497300370caa9ffff5d3f/lib/abort.js#L61-L65
It serves the purpose that I can use the same "abort" logic that is used in a generic fashion also in a cli-context:
https://github.com/consento-org/dat-ssg/blob/6bc7007fe6d109f3535497300370caa9ffff5d3f/bin/dat-ssg#L23
Resulting in a CLI tool that allows graceful abortion of sub-processes in any given situation.
martinheidegger commentedon Mar 11, 2020
@devsnek What work of the TC39 are you referring to?
devsnek commentedon Mar 11, 2020
@martinheidegger for example https://github.com/tc39/proposal-cancellation
ptomato commentedon Apr 3, 2020
Hi, I'd like to work on this!
That sounds good, I'll start out with
fs.createReadStream()as local file operations would seem the easiest place to work out initial problems, then work on the other two.jimmywarting commentedon May 6, 2020
fyi, whatwg streams use AbortSignal also - not just only fetch.
anyhow - maybe we should focus on EventTarget first?
mcollina commentedon May 6, 2020
I'd be in support of adding a cancellable primitive to some base operations such as
readFileorwriteFileand other callback-based APIs. I kindly ask to have it support both EventTarget and EventEmitter, as Node.js is EventEmitter based.Is EventTarget needed at all?
I'd need to know more about adding it to streams and http.
Note that:
Is extremely far from what anybody that serve files with Node.js core would do. Check out https://github.com/pillarjs/send to know more (at the time of this writing, it's 1129 lines of code).
The snipped above works only because
reqandresare connected and they would be destroyed at the same time. If we stream to another location, it will leak a file descriptor and memory:This API needs to be designed correctly to be able to work with on streams. As an example, this would be able to tear down the full pipe on abort:
Note that all the above is already solved in Node.js with
pipeline(), which supports async generators and iterators as well. It's based on the standardization of thestream.destroy(err)operation which is effectively the "abort mechanism" for streams, so the groundwork for adding signals is already done.There is also the question if we should not just add something like:
To achieve our goal.
I would like to see some forward-thinking on how this API will be used by the Node and JS ecosystem and the interaction with the rest of the Node.js Stream libraries.
Would you mind to articulate a bit more on what type of use cases would you like to solve with these and do several examples?
It might well be that we should create a repo and go through some code example before settling into how this API would work.
cc @nodejs/streams @nodejs/http
littledan commentedon May 6, 2020
@mcollina Just about the EventTarget/EventEmitter part: When you construct an AbortController, that will create an AbortSignal, which would be some kind of event thing. I was imagining that this would initially be an EventEmitter, but if we could make it also act as an EventTarget, then this could improve Node/Web compatibility: for the case where you write some code that's cancellable and want to listen for the cancellation being triggered, you'd use
.on()or.addEventListener()depending on whether it's an EventEmitter or EventTarget.ronag commentedon May 6, 2020
I don't think we should expand on the
.pipe(...)or.read(...)API. I would rather we look into adding this topipeline(...)mcollina commentedon May 6, 2020
I'm generically ok with the above, I'd like to see an implementation of this and how it could be used in core.
(We might work on this in a fork of Node.js similarly to how quic and esm have been developed).
7 remaining items
src: Move GetDOMException() into Environment
fs: Expose uv_cancel() method on FSReq
benjamingr commentedon Oct 29, 2020
Just an update here that AbortController and Event are now live and mostly whatwg compatible (we're adding final tests and fixing things). Some Node.js APIs have already gotten AbortSignal support (like EventEmitters and promises versions of timers).
This is a good area to contribute in :]
jimmywarting commentedon Oct 29, 2020
uh? have have timers gotten support for abort signal?
can you now do:
have never heard of "promises versions of timers"
martinheidegger commentedon Oct 29, 2020
@jimmywarting this would be in conflict with JavaScript's setTimeout API:
setTimeout(foo => console.log(foo), 100, 'bar')jimmywarting commentedon Oct 29, 2020
yea, you are right!
i just found the promise version of timers here in https://nodejs.org/dist/latest-v15.x/docs/api/timers.html#timers_timers_promises_api
timersPromises.setTimeout(delay\[, value\[, options\]\])node_fetch previously had a timeout option but was deprecated/removed in v3 in favor of abortSignal + timer was not a spec'ed
A Wishful replacement would be a timer that can return a signal too - something like
See this thread for more information whatwg/fetch#951 (comment)
martinheidegger commentedon Oct 29, 2020
Hmm, I prefer to see the difference in the error message and created recently a
wrapTimeoututil.benjamingr commentedon Oct 29, 2020
@jimmywarting
They have in
require("timers/promises"):]Like Martin said - we can't do this for setTimeout to not break our own code :]
benjamingr commentedon Oct 29, 2020
@jimmywarting
Actually, having a helper on AbortControllers to abort automatically after a time could be interesting/useful though it's probably a good idea to ask that of whatwg (we can extend it and ship a subclass but would rather not until working with spec bodies hasn't worked - and so far whatwg have been very helpful when we presented compelling cases for APIs that would make sense in browsers as well to them).
martinheidegger commentedon Dec 18, 2020
Doing some more work on this, I summarized some findings here, it thought it may give some ideas on improved tooling: https://qiita.com/martinheidegger/items/3e6355e96e85fc1c841e
benjamingr commentedon Dec 18, 2020
We've landed a bunch of apis in fs/child_process/http/http2 with AbortSignal support since this btw, we also added signal support to addEventListener and are working on a way to chain AbortSignals correctly.
benjamingr commentedon Dec 18, 2020
Ooh, cool I just realized both your original examples already work in Node now :] :
jasnell commentedon Jan 4, 2021
Given that progress on this is already well underway, I'm going to close this specific issue.