Asynchronous Programming in Dart
Tags: asynchronous, dart, flutter, mobile
Published: 2023-04-11 08:00:48

## Introduction
I wrote this article based on my interest in asynchronous tasks. Asynchronous programming is important for developers because it allows their applications to perform multiple tasks at the same time, without one task blocking the others. For example, when an application needs to wait for data from a server, asynchronous programming allows it to continue running other tasks, rather than waiting for the data before proceeding. This results in a smoother and more responsive user experience for the application's users.

## Synchronous And Asynchronous Programming
Explaining to a kid, synchronous programming means doing things one at a time, step by step, and waiting for each step to finish before moving on to the next one. It's like doing your homework one page at a time and not moving to the next page until you finish the one you're working on.

On the other hand, asynchronous programming means doing things at the same time, without waiting for each step to finish before starting the next one. It's like listening to music and drawing a picture at the same time, without having to stop one activity to do the other.

In Dart, we can write programs that work synchronously or asynchronously, depending on what we need to do. Synchronous programming is good for simple tasks that can be done quickly, while asynchronous programming is better for tasks that take a long time or need to run in the background while we do something else.

So, when we write a Dart program, we can choose whether to do things one at a time (synchronously) or things at the same time (asynchronously), depending on what we want to achieve.

## What is Future?
Future is used in a dart to denote an operation that is to be completed or not completed. If the task is completed, we get a result otherwise, we return an error. In asynchronous programming, we are forcing the long-processed task to follow an ordered path.

```dart
void main() {
  print('Fetching user data...');
  var userData = fetchUserData();
  print('User data received: $userData');
  print('End of main');
}

Future<String> fetchUserData() {
  // Imagine that this function is more complex and slow.
  return Future.delayed(
    Duration(seconds: 3), 
    () => 'John Doe'
  ); // simulate network delay
}
```

```
Output:

Fetching user data...
User data received: Instance of '_Future<String>'
End of main
```
From the code above, The `fetchUserData()` function returns a `Future` object that will be completed with the result of the network request after a delay of 3 seconds, represented by the `Future.delayed()` method. In this case, the result is simply a string containing the name 'John Doe'.

The output shows that the `main` function is executed synchronously, printing the first message to the console.

Next, the `fetchUserData()` function is called, which returns a `Future<String>` object. The returned future is assigned to the `userData` variable.

The second message is then printed to the console, which shows the value of the `userData` variable. Since `userData` contains a `Future<String>` object, the output displays "Instance of '_Future<String>'" instead of the actual user data.

Finally, the last message is printed to the console, indicating the end of the main function.

The reason for this output is that the `fetchUserData()` function returns a `Future<String>` object, which represents the eventual result of the asynchronous operation. When we assign this future object to a variable and try to print it immediately, we see the type of the object instead of its value.

To access the value of the `Future` object, we need to use the `.then()` method or the `await` keyword in an `async` function.

## Using .then() method
The `.then()` method is used to register a callback function that will be called when a `Future` object completes.

When we have a `Future` object representing an asynchronous operation that will complete at some point in the future, we can use the `.then()` method to attach a callback function that will be executed when the future is resolved with a result.

```dart
void main() {
  print('Fetching user data...');
  fetchUserData().then((userData) {
    print('User data received: $userData');
    print('End of main');
  });
}
```

```
Output:

Fetching user data...
User data received: John Doe
End of main
```
In this example, we use the `.then()` method to register a callback function to be called when the Future returned by `fetchUserData()` is completed. The callback function takes the `userData` string as its argument, which is then printed to the console along with another message indicating the end of the main function.

Note that in the example above, the `print()` statement after the `fetchUserData()` call has been removed, because we are now using the `.then()` method to access the result of the `Future` instead of assigning it to a variable and printing it directly.

What if we place `print()` after the `fetchUserData()` call?

```dart
void main() {
  print('Fetching user data...');
  fetchUserData().then((userData) {
    print('User data received: $userData');
  });
  print('End of main');
}
```

```
Output:

Fetching user data...
End of main
User data received: John Doe
```

The third message ("End of main") is printed before the `Future` is completed, because the call to `fetchUserData()` returns a `Future` immediately, and the `.then()` method is used to register a callback function that will be executed later, when the `Future` is complete.

This example shows how we can use the `.then()` method to perform some action with the result of an asynchronous operation once it becomes available, without blocking the main thread.

For an ordered execution, you need to place the print inside the `.then()` callback after `userData` is retrieved.

## Using async/await
The `async` and `await` keywords are used to write asynchronous code that looks synchronous and is easier to read and understand, without blocking the main thread.

```dart
void main() async {
  print('Fetching user data...');
  var userData = await fetchUserData();
  print('User data received: $userData');
  print('End of main');
}

```

```
Output:

Fetching user data...
End of main
User data received: John Doe
```

The `async` keyword is used to mark the `main` function as asynchronous, which allows us to use the `await` keyword inside the function to pause its execution until a `Future` is completed with a result.

In this case, when the await keyword is used with the `fetchUserData()` function call, it pauses the execution of the `main` function until the Future returned by `fetchUserData()` is resolved with a result.

Once the `Future` is completed, the `await` keyword assigns the result to the `userData` variable, and the execution of the `main` function resumes, printing the second message to the console.

Using `async` and `await` in this way makes it easier to write and read asynchronous code, because it looks similar to synchronous code, and makes it clear what the flow of the code is. It also makes it easier to handle errors and exceptions that might be thrown by asynchronous operations, because they can be caught using a `try-catch` block like in synchronous code.

## Conclusion
In Dart, asynchronous programming can be achieved using `Future` objects, which represent a value that may not be available yet, and the `async` and `await` keywords, which allow us to write asynchronous code that looks synchronous and is easier to read and understand.