-
Notifications
You must be signed in to change notification settings - Fork 161
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
Discussion: How should streams handle Explicit Resource Management #1307
Comments
I had always assumed |
I definitely think that's the easiest approach to take and definitely has the fewest number of potholes ;-) ... and I'm 100% fine if we go with that, I just want to make sure the options are discussed rather than just assumed :-) At least in theory, supporting async disposal of a readable stream could be the equivalent to cancel... {
await using rs = new ReadableStream({ ... });
} By example, Node.js has implemented this pattern for Node.js streams, e.g. // a node.js stream, not a ReadableStream
const r = new stream.Readable;
r[Symbol.asyncDispose](); // cancels and destroys the stream I can see some merit in the approach but I also like the simplicity of just saying, these streams aren't disposable, only the readers/writers are. |
It would help if you could link to the proposal in OP here and in #1741. |
I agree that readers and writers should be sync-disposable. Conceptually, the "resource" they're holding is the lock on the stream, so when you dispose them, they should release that resource through Personally, I would expect readable and writable streams to be async-disposable.
This does mean that anything you do with that stream needs to be inside the block with the
Piping is really tricky though. You're almost required to Oh, and then there's |
Le's focus specifically on If the {
await using readable = new ReadableStream();
const reader = readable.getReader();
// Async dispose of readable fails since it is locked
} If we define reader as being sync disposable such that it releases the lock, then the following should work just fine, releasing the lock then async canceling the stream... {
await using rs = new ReadableStream();
using reader = rs.getReader();
// equivalent to
// reader.releaseLock()
// await rs.cancel();
} If the stream is locked to a reader, then I do think it is worth considering whether {
const rs = new ReadableStream();
await using reader = rs.getReader();
// equivalent to
await reader.cancel();
} This would make sense especially given the idea that the In the pipeTo/pipeThrough scenario, the readable stream remains locked so let dest;
{
await using readable = new ReadableStream();
dest = readable.pipeThrough(new MyTransformStream());
// async dispose fails because readable is locked by the pipe
} But what if the destination is disposable? This should work fine, I think? The {
await using readable = new ReadableStream();
await using dest = readable.pipeThrough(new MyTransformStream());
} For tee, yeah... that's a bit wonky. Not sure what to do here. {
await using readable = new ReadableStream();
const [b1, b2] = readable.tee();
// Async dispose fails because readable is locked
} {
await using readable = new ReadableStream();
await using [b1, b2] = readable.tee();
// Async dispose of the branches works fine... but
// Async dispose of readable still fails since canceling the branches does not release the lock on readable
} Perhaps if we redefined it so that if all branches are canceled/closed the lock on the original stream is released? Another interesting question, which may actually be a question for the TC-39 proposal authors because I'm not sure if this would even be possible... If the block exits because an exception is thrown, should the pending exception be passed into the That is, for instance: {
async using readable = new ReadableStream();
throw new Error('boom');
// Should the async dispose be equivalent to `await readable.cancel(error)` where `error` is the error that is thrown
} |
The discussion so far has convinced me that we should not make |
Heh, I think I managed to convince myself of the same result :-) ... Having readers and writers be disposable so that they release the lock is fine, I think, but even that is probably not critical at all. |
Ok, I think given the discussion and after running through a ton of scenarios in which I've failed to convince myself that it could work, I think we've hit the right conclusion here and will close this issue. I'd say that even making readers and writers disposable is probably not something that is critical at all. Thank you for the consideration on it. I do think we need to have similar conversations and considerations for other web platform apis like |
What is the issue with the Streams Standard?
The Explicit Resource Management proposal (https://github.com/tc39/proposal-explicit-resource-management) is moving forward in TC-39 and implementers are making progress. One thing I'm not sure has been discussed here is how, if at all, the streams spec should implement ERM.
Let's consider
ReadableStream
first. Let's assume that it is decided thatReadableStream
should be disposable...Would it support
Symbol.dispose
(sync disposal) ANDSymbol.asyncDispose
or just async dispose? Let's use an exampleWould we expect that the sync disposer triggers the async cancel algorithm if the
ReadableStream
is still open and readable? Does a sync disposer even make sense in this case or should it always beawait using
?Does it even make sense to support disposing of the
ReadableStream
like this? If it is, would it just simply be the equivalent of callingcancel()
with no reason?Let's consider a reader:
When the block exits, do we release the lock on the stream or do we cancel the stream via the reader? Releasing the lock is synchronous (suggesting that the sync
Symbol.dispose
would be sufficient) but canceling is async (suggesting that we would needSymbol.asyncDispose
).Let's consider a more complex case (this part may be more appropriate for the fetch repo so if we need to discuss it there, let me know)... Let's assume that the stream is attached to a disposable type, and let's assume that
Response
is defined as disposable... what should we expect would happen to the stream in the following case:The same basic questions apply to
WritableStream
, of courseThe text was updated successfully, but these errors were encountered: