Asynchronous programming is a key feature in Dart that allows you to perform tasks without blocking the main thread. This is particularly useful for tasks that take a long time to complete, such as network requests, file I/O, or database operations. In this section, we will cover the basics of asynchronous programming in Dart, including Futures, async/await, and handling asynchronous errors.

Key Concepts

  1. Futures: Represents a potential value or error that will be available at some time in the future.
  2. async/await: Keywords used to write asynchronous code in a more readable and maintainable way.
  3. Error Handling: Techniques to handle errors in asynchronous code.

Futures

A Future in Dart is an object that represents a value that will be available at some point in the future. It can either complete with a value or with an error.

Creating a Future

You can create a Future using the Future constructor or by using async functions.

Future<String> fetchUserOrder() {
  // Imagine that this function is fetching user order from a server
  return Future.delayed(Duration(seconds: 2), () => 'Large Latte');
}

Using then() and catchError()

You can handle the result of a Future using the then() method and handle errors using the catchError() method.

void main() {
  fetchUserOrder().then((order) {
    print('Order: $order');
  }).catchError((error) {
    print('Error: $error');
  });
}

Practical Example

Future<int> fetchData() {
  return Future.delayed(Duration(seconds: 3), () => 42);
}

void main() {
  print('Fetching data...');
  fetchData().then((value) {
    print('Data received: $value');
  }).catchError((error) {
    print('Error: $error');
  });
}

async and await

The async and await keywords provide a more readable way to write asynchronous code. An async function returns a Future, and you can use the await keyword to wait for a Future to complete.

Using async and await

Future<void> printOrderMessage() async {
  var order = await fetchUserOrder();
  print('Order: $order');
}

void main() {
  print('Fetching order...');
  printOrderMessage();
}

Practical Example

Future<int> fetchData() async {
  await Future.delayed(Duration(seconds: 3));
  return 42;
}

void main() async {
  print('Fetching data...');
  try {
    int data = await fetchData();
    print('Data received: $data');
  } catch (error) {
    print('Error: $error');
  }
}

Error Handling in Asynchronous Code

Handling errors in asynchronous code is crucial to ensure your application can gracefully handle unexpected situations.

Using try-catch with async/await

Future<void> printOrderMessage() async {
  try {
    var order = await fetchUserOrder();
    print('Order: $order');
  } catch (error) {
    print('Error: $error');
  }
}

void main() {
  print('Fetching order...');
  printOrderMessage();
}

Practical Example

Future<int> fetchData() async {
  await Future.delayed(Duration(seconds: 3));
  throw Exception('Failed to fetch data');
}

void main() async {
  print('Fetching data...');
  try {
    int data = await fetchData();
    print('Data received: $data');
  } catch (error) {
    print('Error: $error');
  }
}

Exercises

Exercise 1: Fetch User Data

Write a function fetchUserData that simulates fetching user data from a server. The function should return a Future that completes with a user name after a 2-second delay. Use async and await to print the user name.

Future<String> fetchUserData() async {
  // Your code here
}

void main() async {
  // Your code here
}

Solution

Future<String> fetchUserData() async {
  return Future.delayed(Duration(seconds: 2), () => 'John Doe');
}

void main() async {
  print('Fetching user data...');
  try {
    String userName = await fetchUserData();
    print('User name: $userName');
  } catch (error) {
    print('Error: $error');
  }
}

Exercise 2: Handle Errors

Modify the fetchUserData function to throw an error if the user name is not found. Handle the error in the main function using try-catch.

Future<String> fetchUserData() async {
  // Your code here
}

void main() async {
  // Your code here
}

Solution

Future<String> fetchUserData() async {
  return Future.delayed(Duration(seconds: 2), () {
    throw Exception('User not found');
  });
}

void main() async {
  print('Fetching user data...');
  try {
    String userName = await fetchUserData();
    print('User name: $userName');
  } catch (error) {
    print('Error: $error');
  }
}

Conclusion

In this section, we covered the basics of asynchronous programming in Dart, including Futures, async/await, and error handling. Asynchronous programming is essential for building responsive applications, and mastering these concepts will help you write more efficient and maintainable code. In the next section, we will dive into Streams, which allow you to work with sequences of asynchronous events.

© Copyright 2024. All rights reserved