Flutterexperts

Empowering Vision with FlutterExperts' Expertise
Asynchronous In Dart & Flutter

In this article, we will explore the Asynchronous In Dart & Flutter. We see how to execute a demo program in your Dart and Flutter applications.

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table of Contents

Introduction 

Understanding Asynchronous Programming

Advanced Asynchronous Techniques

Handling Asynchronous Operations

Concurrency and Isolates

Comparison of Asynchronous Programming Techniques

Best Practices for Asynchronous Programming

Real-World Examples and Use Cases

Conclusion

References


Introduction

Asynchronous programming is a crucial aspect of modern software development, allowing applications to perform multiple tasks concurrently without blocking the main thread. In Dart and Flutter, mastering asynchronous programming is essential for handling network requests, file I/O, and user interactions In this blog, we’ll explore the fundamentals of asynchronous programming in Dart and how it applies to Flutter development. We’ll also compare different asynchronous programming techniques, cover advanced techniques, and discuss their pros, cons, and limitations.


Understanding Asynchronous Programming

At its core, asynchronous programming deals with code execution that doesn’t happen sequentially. Unlike synchronous code, which executes line by line, asynchronous code allows tasks to run concurrently, improving performance and responsiveness. In Dart, asynchronous programming revolves around asynchronous functions, futures, and streams.

Asynchronous Functions

  • async: Marks a function as asynchronous, enabling the use of await inside it.
  • await: Suspends the execution of an asynchronous function until a Future completes.
  • Future: Represents a potential value or error that will be available in the future.
  • FutureOr: A type that represents either a Future or a non-future value.
  • Stream: A sequence of asynchronous events over time.
  • Isolate: A lightweight process that runs concurrently with other isolates, enabling true parallelism.

Example: Fetching Data from an API:

Future<void> fetchData() async {
try {
var response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
print('Data received: $data');
} else {
print('Failed to load data');
}
} catch (e) {
print('Error fetching data: $e');
}
}

Advanced Asynchronous Techniques

Beyond the basics, Dart offers advanced techniques for handling asynchronous operations efficiently and effectively:

  • Futures and Error Handling

Chaining Futures: Chain multiple asynchronous operations using then and handle errors with catchError.

Future<void> fetchAndProcessData() {
return fetchDataFromAPI()
.then((data) => processData(data))
.catchError((error) => handleError(error));
}

Timeouts: Implement timeouts for Futures to prevent blocking operations.

Future<void> fetchDataWithTimeout() async {
try {
var data = await fetchDataFromAPI().timeout(Duration(seconds: 5));
print('Data received: $data');
} catch (e) {
print('Operation timed out or failed: $e');
}
}
  • Streams and Reactive Programming

Stream Controllers: Create and manage streams using StreamController to handle continuous data flow.

StreamController<int> streamController = StreamController<int>();

void addDataToStream() {
for (int i = 0; i < 5; i++) {
streamController.add(i);
}
streamController.close();
}

void listenToStream() {
streamController.stream.listen((data) {
print('Data received: $data');
});
}

void main() {
addDataToStream();
listenToStream();
}

Stream Transformers: Use stream transformers for filtering, transforming, and combining stream data.

Stream<int> numberStream = Stream.fromIterable([1, 2, 3, 4, 5]);

Stream<int> transformedStream = numberStream.transform(StreamTransformer.fromHandlers(
handleData: (data, sink) {
sink.add(data * 2);
}
));

transformedStream.listen((data) {
print('Transformed data: $data');
});
  • Isolates and Parallelism

Spawning Isolates: Spawn isolates for parallel execution of CPU-bound and I/O-bound tasks.

import 'dart:isolate';

void isolateFunction(SendPort sendPort) {
int result = 0;
for (int i = 0; i < 1000000000; i++) {
result += i;
}
sendPort.send(result);
}

void main() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(isolateFunction, receivePort.sendPort);
int result = await receivePort.first;
print('Result from isolate: $result');
}

Communication: Exchange messages between isolates using SendPort and ReceivePort.

import 'dart:isolate';

void isolateFunction(List<dynamic> args) {
SendPort sendPort = args[0];
int data = args[1];
int result = data * 2;
sendPort.send(result);
}

void main() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(isolateFunction, [receivePort.sendPort, 10]);
int result = await receivePort.first;
print('Result from isolate: $result');
}

Handling Asynchronous Operations

Working with asynchronous operations in Dart involves proper handling to ensure smooth execution and error management:

  • Use Future for single asynchronous operations and await to wait for their completion.
  • Chain asynchronous operations using then and handle errors with catchError.
  • Wrap asynchronous code in a try-catch block to handle exceptions gracefully.

Example:

Future<void> fetchData() async {
try {
var data = await fetchDataFromServer();
print('Data received: $data');
} catch (e) {
print('Error fetching data: $e');
}
}

Concurrency and Isolates

Concurrency

Concurrency refers to the ability of a system to execute multiple tasks concurrently without blocking each other. In Dart, concurrency is achieved through asynchronous programming techniques like Futures, Streams, and Isolates.

  • Pros: Lightweight, easy to implement, suitable for handling asynchronous operations.
  • Cons: Limited scalability and concurrency compared to isolates, cannot achieve true parallelism.

Example: Concurrent Execution with Futures

void main() {
print('Start');
fetchData().then((data) {
print('Data: $data');
});
print('End');
}

Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'Hello, World!';
}

In the above example, fetchData() is executed asynchronously, allowing the program to continue executing other tasks while waiting for the data to be fetched.

Isolates

Isolates are independent workers that run concurrently with the main program and each other, enabling true parallelism in Dart. Each isolate has its memory heap and runs in its thread, allowing heavy computation tasks to be executed in parallel without blocking the main thread.

  • Pros: Enables true parallelism, ideal for CPU-bound tasks, improves performance and scalability.
  • Cons: Higher overhead due to separate memory heaps, requires message passing for communication between isolates.

Example: Using Isolates for Parallel Execution

In this example, we spawn a new isolate using Isolate.spawn() and communicate with it using message passing via SendPort and ReceivePort.

import 'dart:isolate';

void main() async {
final receivePort = ReceivePort();
await Isolate.spawn(echo, receivePort.sendPort);
final sendPort = await receivePort.first;
final message = await sendData(sendPort, 'Hello, Isolate!');
print('Received: $message');
}

void echo(SendPort sendPort) {
final receivePort = ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((message) {
sendPort.send(message);
});
}

Future<String> sendData(SendPort sendPort, String message) {
final receivePort = ReceivePort();
sendPort.send([message, receivePort.sendPort]);
return receivePort.first;
}

Difference Between Concurrency and Isolates

  • Concurrency: Involves executing multiple tasks concurrently within the same thread, often achieved through asynchronous programming techniques like Futures and Streams.
  • Isolates: Enable true parallelism by running independent workers in separate threads with their own memory heap, allowing heavy computation tasks to be executed in parallel without blocking the main thread.

Comparison of Asynchronous Programming Techniques

Let’s compare different asynchronous programming techniques in Dart based on their strengths, weaknesses, and use cases:

Futures

  • Pros: Simple and easy to understand, suitable for single asynchronous operations.
  • Cons: Limited concurrency and scalability, cannot handle multiple tasks concurrently without additional techniques.
  • Example:
Future<void> fetchData() async {
final data = await fetchDataFromAPI();
print('Data: $data');
}

Streams

  • Pros: Ideal for handling continuous data flow and event-driven programming.
  • Cons: More complex than Futures, managing subscriptions and cancellations can be challenging.
  • Example:
Stream<int> counterStream() async* {
for (int i = 0; i < 10; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}

Isolates

  • Pros: Enables true parallelism and scalability, suitable for CPU-bound and I/O-bound tasks.
  • Cons: Requires communication overhead between isolates and careful management of shared resources.
  • Example:
Future<void> processDataInIsolate() async {  
final isolate = await Isolate.spawn(doHeavyTask, 'data');
isolate.addErrorListener(...);
isolate.addMessageListener(...);
}

Best Practices for Asynchronous Programming

To write efficient and maintainable asynchronous code, follow these best practices:

  • Avoid Nesting: Refrain from nesting asynchronous operations to maintain code readability.
  • Error Handling: Handle errors and exceptions gracefully to prevent application crashes and unexpected behavior.
  • Cancellation: Use cancellation tokens to cancel long-running operations and free up resources.
  • Performance Optimization: Minimize unnecessary async/await calls and leverage parallelism for improved performance.

Real-World Examples and Use Cases

Let’s explore some real-world scenarios where asynchronous programming is crucial:

  • Network Requests: Fetch data from APIs asynchronously to maintain app responsiveness.
Future<void> fetchNetworkData() async {
try {
var response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
print('Data received: $data');
} else {
print('Failed to load data');
}
} catch (e) {
print('Error fetching data: $e');
}
}
  • File I/O: Read and write files asynchronously to prevent blocking the main thread.
import 'dart:io';

Future<void> readFile() async {
try {
var file = File('example.txt');
String contents = await file.readAsString();
print('File contents: $contents');
} catch (e) {
print('Error reading file: $e');
}
}

Future<void> writeFile() async {
try {
var file = File('example.txt');
await file.writeAsString('Hello, Dart!');
print('File written successfully');
} catch (e) {
print('Error writing file: $e');
}
}
  • User Interactions: Handle user inputs and gestures asynchronously to provide a smooth and interactive user experience.
Future<void> handleUserInput() async {
try {
var userInput = await getUserInput();
print('User input received: $userInput');
} catch (e) {
print('Error handling user input: $e');
}
}

Future<String> getUserInput() async {
// Simulate a delay to mimic user input
await Future.delayed(Duration(seconds: 1));
return 'User input';
}

Conclusion

Asynchronous programming is a powerful tool for building responsive and efficient Dart and Flutter applications. By mastering the fundamentals of asynchronous programming and following best practices, developers can create high-performance applications that provide a seamless user experience. Continue exploring asynchronous programming techniques and experimenting with Dart and Flutter to unlock their full potential.


References:

  • Dart Language Asynchronous Programming
  • Flutter Asynchronous Programming
  • Effective Dart: Asynchronous Programming
  • Dart Isolates

❤ ❤ Thanks for reading this article ❤❤

If I need to correct something? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally rich apps. Hire a Flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on Facebook, GitHub, Twitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.


Leave comment

Your email address will not be published. Required fields are marked with *.