What Are Futures?
In Dart, futures are very simple. As the word itself suggests, they happen in the future.
An Analogy to Understand Futures
Let’s consider this analogy:
You have a friend, and you’re working on a project. You tell your friend, "I want a glass of water," and then you continue working on your project. You don’t wait for your friend to bring the glass of water; you keep working. When your friend eventually brings the water, you pause your work, drink the water, and then continue with your project.
This is how futures work. Futures in Dart mean that a function will return a value at some point in the future. Your UI won’t stop while waiting for that future to complete. Otherwise, it would block everything, which is not efficient.
That’s why this is called asynchronous programming.
Synchronous: You wait for your friend to bring the water before continuing your work.
Asynchronous: You keep working and pause only when the water arrives.
Creating a Future in Dart
To create a future in Dart is very easy. All you need to do is add the async
keyword to a function. This tells Dart that the function is asynchronous.
For example:
Let’s say you have a function named add
that adds two values. After some time, it returns the result. Since it does not return the integer immediately, it wraps the integer in a Future
.
Here’s how to define and use an asynchronous function in Dart:
Future<int> add(int a, int b) async {
return a + b; // This will return the result in the future
}
// Calling the asynchronous function
void main() async {
int result = await add(2, 3); // Using 'await' to pause for the future's result
print(result); // Output: 5
}
Why Do We Use await
?
The reason we use await
is like saying to the dart lets wait for the future to complete then we will execute code below it . But remmber its a async task so the UI will keep working as it should be only the code in that function will wait for it to return .
What Are Streams?
Streams in Dart are similar to futures, but with one key difference:
Futures return one value at a particular point in time.
Streams return multiple values over a period of time.
An Analogy to Understand Streams
Think of a stream of water. It flows continuously over time, right? Similarly, in Dart, your data flows over a period of time, and you can listen to that data.
For example, when you’re reading a file, you don’t receive the entire file data at once. Instead, it’s sent in chunks over time, making it faster and more efficient.
Creating a Stream in Dart
To create a stream in Dart, you can use a StreamController
. You can then listen to the stream and add data to it.
Here’s an example:
import 'dart:async';
void main() {
// Create a StreamController
StreamController<int> controller = StreamController<int>();
// Listen to the stream
controller.stream.listen(
(data) => print("Data received: $data"), // Handle incoming data
onError: (error) => print("Error: $error"), // Handle errors
onDone: () => print("Stream closed"), // Handle stream completion
);
// Add data to the stream
controller.add(1);
controller.add(2);
controller.add(3);
// Close the stream
controller.close();
}
Handling Errors and Completion in Streams
When working with streams, you have two important functions:
onError
: Called when the stream encounters an error.onDone
: Called when the stream has completed sending data.
This allows you to handle errors gracefully and perform actions when the stream ends.
Default Stream Behavior: One Listener
By default, only one person can listen to a stream. Let’s take another analogy:
Imagine your friend is talking to you. You’re the only one listening to them by default.
But if you want multiple people to listen to your friend, you can use a broadcast stream.
Broadcast Streams
To create a broadcast stream, use the controller.stream
.asBroadcastStream()
method. This allows multiple listeners to listen to the same stream simultaneously.
Example:
import 'dart:async';
void main() {
// Create a StreamController
StreamController<int> controller = StreamController<int>();
// Convert it to a broadcast stream
Stream<int> broadcastStream = controller.stream.asBroadcastStream();
// Listen to the broadcast stream
broadcastStream.listen((data) => print("Listener 1: $data"));
broadcastStream.listen((data) => print("Listener 2: $data"));
// Add data to the stream
controller.add(1);
controller.add(2);
// Close the stream
controller.close();
}
Now, both listeners can receive the data sent by the stream.
By understanding futures and streams in Dart, you can efficiently handle asynchronous tasks and data flows in your applications. Keep experimenting with these concepts to master them!