Futures play an integral part in asynchronous programming in Dart. They return single data or error events asynchronously. Streams function in a similar way, but they deal with sequences of events instead of single events. So, streams are to future objects what iterators are to integers. Streams emit data events (or elements), error events, and “done” events to notify the end of the event flow.
We can iterate over events in a stream using an asynchronous for loop or an await for
loop. The program below uses this loop to print the cumulative sum of even numbers from to :
import 'dart:async';//This function yields a stream of even integers between 1 and num inclusiveStream<int> iterateEvenStream(int num) async* {for (int i = 1; i <= num; i++) {if (i%2 == 0){print("Yielding ${i}");yield i;}}}//This function recieves a stream of integer events and returns their sum once the//stream ends//Note: The async keyword is needed for the await for loopFuture<int> evenSumStream(Stream<int> stream) async {var evenSum = 0;await for (var val in stream) {evenSum += val;print("Cumulative sum after adding ${val}: ${evenSum}\n");}return evenSum;}main() async {var upperLimit = 15;var stream = iterateEvenStream(upperLimit);var finalEvenSum = await evenSumStream(stream);print("Final sum of even numbers between 1 and ${upperLimit}: ${finalEvenSum}");}
Just as with futures, actions are decided in advance when an event is emitted. If we want to call a function every time a stream emits an event, we can use Dart’s StreamSubscription object and call its listen()
method. The template for this method is:
StreamSubscription<T> listen(void Function(T event) onData,
{Function onError, void Function() onDone, bool cancelOnError});
The only required parameter for the method above is the function that’s called when the data arrives. The following example shows how listen()
can be put to use:
StreamSubscription<int> subscription = stream.listen((data) { //this block is executed when data event is recieved by listenerprint('Data: $data');},onError: (err) { //this block is executed when error event is recieved by listenerprint('Error: ${err}');},cancelOnError: false, //this decides if subscription is cancelled on error or notonDone: () { //this block is executed when done event is recieved by listenerprint('Done!');},);
The stream in the example above is a single subscription stream. This means that the stream has been set up for an individual listener to subscribe to once throughout the stream’s lifetime. So, a repeated subscription or multiple listeners will result in an error. By default, all streams are single subscription, so they only emit events when a listener is subscribed.
If our implementation requires multiple listeners, we can use Dart’s asBroadcastStream
method to create a broadcast stream on top of a single subscription stream. A broadcast stream can have multiple listeners, and if an event arrives while there is no listener, the stream tosses the event out.
To explore more features that streams have, go to Dart’s official documentation
Free Resources