Understanding Asynchronous Programming in Dart: Futures and Streams Explained

mateus-ic1101

Mateus 🇧🇷

Posted on October 4, 2024

Understanding Asynchronous Programming in Dart: Futures and Streams Explained

Understanding Future and Asynchronous Programming in Dart

The concept of a Future in Dart represents an operation that will complete at some point in the future, asynchronously. It allows the program to run without blocking other code while waiting for a result.

In asynchronous operations, the program continues executing normally while the task (such as a data request) runs in the background. Once the task is finished, the Future returns the result. This prevents the program from "freezing" while waiting.

Example of asynchronous usage without await:

void main() async {
  print('Start of operation');
  setName();
  print('End of the main function');
}

Future<String> setName() => Future.delayed(Duration(seconds: 3), () {
      print('Operation completed');
      return 'Will 01';
    });
Enter fullscreen mode Exit fullscreen mode

Output:

Start of operation
End of the main function
Operation completed
Exited.
Enter fullscreen mode Exit fullscreen mode

In the previous example, the function call to print with the text "End of the main function" was executed before the print statement inside the setName() function. This happened because Dart's execution model is based on a single-threaded event loop with an asynchronous non-blocking nature. When the setName() function is called, it returns a Future immediately, and the Future.delayed operation is scheduled to run after a 3-second delay. However, the main() function does not wait for this delayed operation to complete before continuing its execution. As a result, the next print statement ("End of the main function") is executed right away.

Technically, this behavior is governed by the event loop and the concept of asynchronous execution. When the Future.delayed is invoked, it schedules a task to be completed after 3 seconds but doesn't block the current thread. This allows Dart to continue executing the remaining code immediately, without waiting for the asynchronous operation to complete.

To ensure that the main() function waits for the result of setName(), you can use the async and await keywords, which will pause the execution of main() until the Future is resolved.

Example of asynchronous usage with await:

We will add this line String name = await setName(); before the last print statement.

void main() async {
  print('Start of operation');

  String name = await setName();

  print('End of the main function');
}

Future<String> setName() => Future.delayed(Duration(seconds: 3), () {
      print('Operation completed');
      return 'Will 01'; 
    });
Enter fullscreen mode Exit fullscreen mode

Output:

Start of operation
Operation completed
End of the main function

Exited.
Enter fullscreen mode Exit fullscreen mode

The use of await in the line String name = await setName(); causes the main() function to pause execution until the setName() function completes and returns a value. This is why the order of output changes compared to the previous example. When await is used, the program waits for the asynchronous operation to finish before proceeding, ensuring that the statement print('End of the main function'); is executed only after the Operation completed message is printed. This reflects Dart's asynchronous programming model, where await allows for more readable and sequential-looking code while still handling operations that take time to complete.


Understanding Streams in Dart

In Dart, a Stream is a sequence of asynchronous events or data that can be listened to. Streams are useful for handling data that arrives over time, such as user input, web requests, or other asynchronous operations.

A StreamController is an object that allows you to create a stream and control its input. It provides methods to add data to the stream and manage its lifecycle. You can think of it as a bridge between producing and consuming asynchronous data.

Example:

void main() {
  final StreamController<String> controller = StreamController<String>();

  controller.stream.listen((data) {
    print('Listener received: $data');
  });

  addDataToStream(controller);

  Future.delayed(Duration(seconds: 5), () {
    controller.close();
    print('Stream closed.');
  });
}

void addDataToStream(StreamController<String> controller) {
  Timer.periodic(Duration(seconds: 1), (timer) {
    if (timer.tick <= 5) {
      controller.sink.add('Data item ${timer.tick}');
    } else {
      timer.cancel();
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

Output:

Listener received: Data item 1
Listener received: Data item 2
Listener received: Data item 3
Listener received: Data item 4
Listener received: Data item 5
Stream closed.

Exited.
Enter fullscreen mode Exit fullscreen mode

This code demonstrates how to use a StreamController in Dart to manage data streams. A StreamController allows you to produce and listen to data events. In this example, the controller is created to handle a stream of String data.

The listener is attached to the stream using controller.stream.listen(), which listens for incoming data. Every time new data is added to the stream, the listener prints it to the console.

The function addDataToStream is responsible for adding data to the stream periodically. It uses Timer.periodic to simulate adding a new data item every second for 5 seconds. The data is sent into the stream via controller.sink.add().

After 5 seconds, the stream is closed using controller.close(). This ensures no more data can be added to or received from the stream. The stream closure is also confirmed with a message printed to the console.

Conclusion

Understanding Futures and Streams is essential for effective asynchronous programming in Dart. By mastering these concepts, you empower yourself to build responsive and efficient applications that can handle complex data flows seamlessly. As you integrate these patterns into your development practice, I invite you to share your insights and experiences.

Your feedback is invaluable! If you have suggestions for improvements, questions, or if you identify any inaccuracies, please don’t hesitate to reach out.

💖 💪 🙅 🚩
mateus-ic1101
Mateus 🇧🇷

Posted on October 4, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related