I know I have to cancel Stream
Subscriptions when I no longer want to receive any events. Do I have to this even after I receive a 'Done' event? Or do I get memory leaks?
What happens to Streams that are passed to addStream
of another Stream? Are they automatically canceled?
Same Question on the StreamSink
side do I have to close them if the stream is already done?
Streams provide an asynchronous sequence of data. Data sequences include user-generated events and data read from files. You can process a stream using either await for or listen() from the Stream API. Streams provide a way to respond to errors.
Short-answer: no, but you should. Nothing in the contract of either StreamSubscription
or StreamSink
requires closing the resources, but some use cases can lead to memory leaks if you don't close them, even though in some cases, doing so might be confusing. Part of the confusion around these classes is that they are overloaded, and handle two fairly distinct use cases:
Let's tackle these subjects one at a time, first, StreamSubscription
:
StreamSubscription
When you listen
to a Stream
, you receive a StreamSubscription
. In general, when you are done listening to that Stream
, for any reason, you should close the subscription. Not all streams will leak memory if choose not to, but, some will - for example, if you are reading input from a file, not closing the stream means the handle to the file may remain open.
So, while not strictly required, I'd always cancel
when done accessing the stream.
StreamSink
The most common implementation of StreamSink
is StreamController
, which is a programmatic interface to creating a Stream
. In general, when your stream is complete (i.e. all data emitted), you should close the controller.
Here is where it gets a little confusing. Let's look at those two cases:
Imagine you were creating an API to asynchronously read a File line-by-line:
Stream<String> readLines(String path);
To implement this, you might use a StreamController
:
Stream<String> readLines(String path) { SomeFileResource someResource; StreamController<String> controller; controller = new StreamController<String>( onListen: () { someResource = new SomeFileResource(path); // TODO: Implement adding to the controller. }, ); return controller.stream; }
In this case, it would make lots of sense to close the controller
when the last line has been read. This gives a signal to the user (a done event) that the file has been read, and is meaningful (you can close the File resource at that time, for example).
Imagine you were creating an API to listen to news articles on HackerNews:
Stream<String> readHackerNews();
Here it makes less sense to close the underlying sink/controller. Does HackerNews ever stop? Event streams like this (or click
handlers in UI programs) don't traditionally "stop" without the user accessing for it (i.e cancelling the StreamSubscription
).
You could close the controller when you are done, but it's not required.
Hope that makes sense and helps you out!
I found in my case that if I have code like this:
Stream<String> readHackerNews(String path) { StreamController<String> controller = StreamController<String>(); ...... return controller.stream; }
I see a warning message "Close instance of 'dart.core.Sink'." in the Visual Studio Code. In order to fix this warning I added
controller.close()
to the event handler for the OnCancel event, see below:
Stream<String> readHackerNews(String path) { StreamController<String> controller = StreamController<String>(); //TODO: your code here controller.onCancel = () { controller.close(); }; return controller.stream; }
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With