Future in dart: When to use async, await, and then
Tags: dart, flutter, tutorial, learning
Published: 2021-08-07 05:45:35

There are multiple ways to implement asynchronous logic in dart. Dart provides three keywords for this: `async`, `await`, and `then`.  
In this blog, we'll learn how and when to use them according to our needs.

## Overview

- [What is asynchronous programming?](#asynchronous-programming)
- [Asynchronous in dart](#asynchronous-in-dart)
- [Using `then`](#using-then)
- [Using `async-await`](#using-async-await)
- [Code Samples](#code-samples)  
  - [Sample 1](#sample-1)
  - [Sample 2](#sample-2)
- [Conclusion](#conclusion)
- [Final Note](#final-note)

## What is asynchronous programming? <a id="asynchronous-programming"></a>
Asynchronous programming refers to a block of code that runs in parallel with other blocks of code. As a result, we can do several things at the same time.

<img width="100%" style="width:100%" src="https://media.giphy.com/media/QX15lZJbifeQPzcNDt/giphy.gif">

Source: [GIPHY](https://giphy.com/gifs/monday-computer-molehill-QX15lZJbifeQPzcNDt)
  
  
For example, say we have an API request that fetches a list of recipes. This network call might take some time. During this fetching, we have a text field where a user can type into.  
Our application is interacting with the user as well as fetching recipes simultaneously.  
Another way to think about this is, doing some background tasks separately without disrupting the main task flow.

## Asynchronous in dart <a id="asynchronous-in-dart"></a>
Dart gives us two ways to implement asynchronous logic: 
- [Stream](https://api.flutter.dev/flutter/dart-async/Stream-class.html)
- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)

In this blog, we'll discuss `Future`.  
`Future` class has various constructors that we can use. There are certain keywords that we need to implement when using them. The keywords are: `async`, `await`, and `then`.

## Using `then` <a id="using-then"></a>

`then` works on the concept of callbacks. The callback is exactly what it says it is. Calling back (executing) a block of code sometime later.  

Let's say we are fetching a list of recipes. This returns a `Future`.  
- After the `Future` is completed (we've fetched the recipes), we want to display a message to the user.  
- During this fetching, we don't want to stick there but continue to execute our next block of code.  

Let's look at the following code. For simplicity, we'll use print statements. 

> You can run the code sample below in [dartpad](https://dartpad.dev/). Just copy-paste the code in dartpad and run it.

{% gist https://gist.github.com/nicks101/29acd0a7b27075ca85a898394267797a %}


Output:
```dart
start fetching recipes
a random computation
recipes fetched
after fetching recipes
```

Let's understand the code.  

In line 4, we are fetching our recipes.

>Here, we've used the `Future.delayed` constructor for demo purposes. Using this, we can specify the time we want to wait for (here we are waiting for 1 second).
Refer [here](https://api.flutter.dev/flutter/dart-async/Future/Future.delayed.html), if you want to know more about this function.

In lines 6-8, we are giving a callback (a block of code) that is supposed to run when the recipes are fetched (or `Future` is completed).  
The code after line 8 should not wait for the recipes to be fetched but continue to execute.

Analyzing the output:
1. The first console print statement is `start fetching recipes` as expected.  
2. The second console print statement is `a random computation`. This is the desired result. The `Future` takes time to complete and the rest of the code (below line 8) does not wait for it to complete but executes.  
3. After a while, `recipes fetched` and `after fetching recipes` executes which were the callbacks for the `Future`.

---

So, `then` fits here perfectly.

Now let's see another use case.  
- After the `Future` is completed (we've fetched the recipes), we want to display a message to the user.  
- We want to wait for the recipes to be fetched and not execute the lines below 8.

The easiest way would be to put all the code inside the `then` block.  

{% gist https://gist.github.com/nicks101/d9c307faf50a3ee8ed4b981dccd6ef58 %}

Output:
```dart
start fetching recipes
recipes fetched
after fetching recipes
a random computation
```

We've just added everything under the `then` block. Now, the `a random computation` is executed after the recipes are fetched.

There's a better way to implement this use case.  
Since we want to wait for the recipes to be fetched and not execute the code below, we could use simpler syntax.  
And that's what `async` and `await` are. An alternative syntax to implement asynchronous logic.

## Using `async-await` <a id="using-async-await"></a>

Let's implement our second use case again. 

{% gist https://gist.github.com/nicks101/963f34cd8ee40848c33ecd9452191b92 %}

Output:
```dart
start fetching recipes
recipes fetched
after fetching recipes
a random computation
```

This time, we've not used any callbacks explicitly like the `then` block. Instead, we've added `async` in line 1 and `await` in line 4.  
- `async` keyword tells dart that this function might use asynchronous logic. So `void main` has the `async` keyword.
- `await` keyword tells dart that the following statement will return a `Future`. The `Future` should be completed and then the code below will be executed.

Let's look at some more examples.

## Code Samples <a id="code-samples"></a>

As seen above, we can have multiple implementations for the same logic. Let's look at some code samples and try to simplify them.
  
### Sample 1 <a id="sample-1"></a>

```dart
void main() async {
  await Future.delayed(Duration(seconds: 1), () {
    print('inside delayed');
  }).then((_) {
    print('inside then');
  });

  print('after delayed');
}
```

The `then` block is redundant here. Since we're `awaiting` the `Future`, the `then` callback can be implemented like a normal code of block.

Simplified: 
```dart
void main() async {
  await Future.delayed(Duration(seconds: 1), () {
    print('inside delayed');
  });

  print('inside then');

  print('after delayed');
}
```

### Sample 2 <a id="sample-2"></a>

```dart
void main() {
  Future.delayed(Duration(seconds: 1), () {
    print('inside delayed 1');
  }).then((_) {
    print('inside then 1');

    Future.delayed(Duration(seconds: 1), () {
      print('inside delayed 2');
    }).then((_) {
      print('inside then 2');
    });
  });

  print('after delayed');
}
```

There is a nesting of `then` blocks here. We can use `async`, `await` syntax inside a `then` block also.

Simplified: 
```dart
void main() {
  Future.delayed(Duration(seconds: 1), () {
    print('inside delayed 1');
  }).then((_) async {
    print('inside then 1');

    await Future.delayed(Duration(seconds: 1), () {
      print('inside delayed 2');
    });

    print('inside then 2');
  });

  print('after delayed');
}
```

I hope the examples helped in understanding the various syntaxes.  
And that's it for asynchronous programming in dart using `Future`.

## Conclusion <a id="conclusion"></a>

- We can use `then` syntax or `async-await` syntax or both to implement asynchronous logic.  
- Any syntax can be used to achieve our desired result. But we should also focus on code-readability.

> For further reading, you can refer to dart [official docs](https://dart.dev/codelabs/async-await).

## Final Note <a id="final-note"></a>

Thank you for reading this article. If you enjoyed it, consider sharing it with other people.  
If you find any mistakes, please let me know.  
Feel free to share your opinions below.