Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart: Do I have to cancel Stream subscriptions and close StreamSinks?

Tags:

stream

dart

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?

like image 632
Thomas Avatar asked Apr 17 '18 13:04

Thomas


People also ask

How stream does work in Dart?

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.


2 Answers

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:

  1. Resource streams (like file I/O, network access)
  2. Event streams (like click handlers)

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:

File I/O

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).

Events

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!

like image 96
matanlurey Avatar answered Sep 16 '22 11:09

matanlurey


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!

like image 41
Sergey Avatar answered Sep 20 '22 11:09

Sergey