— Dart, Streams, Asynchronous Programming — 3 min read
Dart is a versatile programming language that offers powerful features for asynchronous programming. One such feature is Dart Streams, which provide a way to handle sequences of asynchronous events in an elegant and efficient manner. In this tutorial, we will dive into the world of Dart Streams and understand how they can simplify your asynchronous code. So let's get started!
Streams in Dart represent sequences of asynchronous events. They allow you to process data as it becomes available over time, rather than waiting for all the data to be ready upfront. Streams are widely used for handling various types of asynchronous operations, such as network requests, file I/O, user input, and more.
A stream can emit multiple values over time, and you can listen to these values using stream listeners. The listeners receive events whenever new data is available or when the stream is done emitting events. This makes streams a powerful tool for handling asynchronous operations and reacting to real-time data.
To create a stream in Dart, you can use the Stream
class provided by the dart:async
library. Here's an example of creating a simple stream that emits a sequence of integers:
1import 'dart:async';2
3void main() {4 final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);5
6 stream.listen((int value) {7 print(value);8 });9}
In this example, we create a stream using the Stream.fromIterable
constructor, which takes an iterable as input. The listen
method is then called on the stream to start listening for events. Whenever a new value is emitted by the stream, the provided callback function will be invoked.
Dart Streams provide several built-in methods that allow you to transform and manipulate the data emitted by a stream. These methods can be chained together to build complex data processing pipelines. Let's take a look at a few commonly used methods:
map
: Applies a transformation to each event in the streamThe map
method allows you to apply a transformation function to each event emitted by the stream. It returns a new stream that emits the transformed events. Here's an example that doubles the values of a stream of integers:
1import 'dart:async';2
3void main() {4 final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);5
6 stream.map((int value) => value * 2).listen((int doubledValue) {7 print(doubledValue);8 });9}
In this example, the map
method is used to transform each value emitted by the stream by multiplying it by 2. The resulting stream emits the doubled values, which are then printed.
where
: Filters events based on a predicateThe where
method allows you to filter events based on a given condition or predicate function. It returns a new stream that only emits the events satisfying the condition. Here's an example that filters out even numbers from a stream of integers:
1import 'dart:async';2
3void main() {4 final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);5
6 stream.where((int value) => value.isOdd).listen((int oddValue) {7 print(oddValue);8 });9}
In this example, the where
method filters out even numbers by checking if each value is odd. Only the odd values are emitted by the resulting stream.
asyncMap
: Applies an asynchronous transformation to each eventThe asyncMap
method allows you to apply an asynchronous transformation function to each event in the stream. It returns a new stream that emits the transformed events when the transformations complete. Here's an example that performs an async HTTP request for each URL emitted by a stream:
1import 'dart:async';2import 'package:http/http.dart' as http;3
4void main() {5 final stream = Stream<String>.fromIterable([6 'https://example.com',7 'https://google.com',8 'https://openai.com'9 ]);10
11 stream.asyncMap((String url) => http.get(Uri.parse(url))).listen((response) {12 print(response.body);13 });14}
In this example, the asyncMap
method is used to perform an HTTP GET request for each URLemitted by the stream. The http.get
function returns a Future
representing the asynchronous operation, and the resulting stream emits the response when each request completes.
When working with streams, it's important to handle errors that may occur during asynchronous operations. Dart Streams provide the onError
method, which allows you to specify an error handler for the stream. Here's an example:
1import 'dart:async';2
3void main() {4 final stream = Stream<int>.fromIterable([1, 2, 3]);5
6 stream.map((int value) => value * 2).listen(7 (int doubledValue) {8 print(doubledValue);9 },10 onError: (error) {11 print('Error occurred: $error');12 }13 );14}
In this example, the onError
callback is invoked if any error occurs during the stream processing. You can use this callback to handle and react to errors in a graceful manner.
Streams in Dart need to be closed once you're done using them to free up resources and prevent memory leaks. The StreamSubscription
returned by the listen
method provides a cancel
method that you can call to close the stream. Alternatively, you can use the await for
loop to automatically close the stream when all events have been processed. Here's an example:
1import 'dart:async';2
3void main() async {4 final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);5
6 final subscription = stream.listen((int value) {7 print(value);8 });9
10 // Do some processing...11
12 await subscription.cancel();13}
In this example, the stream is closed by calling the cancel
method on the subscription
object. Alternatively, you can use await for
loop syntax like this:
1import 'dart:async';2
3void main() async {4 final stream = Stream<int>.fromIterable([1, 2, 3, 4, 5]);5
6 await for (int value in stream) {7 print(value);8 }9}
With await for
, the stream is automatically closed when all events have been processed.
Dart Streams provide a powerful tool for handling asynchronous events and data processing in an efficient and flexible manner. They allow you to work with sequences of events as they occur, enabling real-time data handling and reacting to changes in your applications. By understanding the basics of Dart Streams and utilizing their various methods, you can take full advantage of asynchronous programming in Dart.