Future in dart: When to use async, await, and then

Future in dart: When to use async, await, and then

Asynchronous programming in dart

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 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.


Source: GIPHY

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

Dart gives us two ways to implement asynchronous logic:

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

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.

Run the code by clicking the Run button below.

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, 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.
Run the code.

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

Let's implement our second use case again. Run the code.

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

As seen above, we can have multiple implementations for the same logic. Let's look at some code samples and try to simplify them.

You can run the code sample below in dartpad. Just copy-paste the code in dartpad and run it.

Sample 1

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:

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

  print('inside then');

  print('after delayed');
}

Sample 2

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:

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

  • 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.

Final Note

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.

Did you find this article valuable?

Support Nikki Goel by becoming a sponsor. Any amount is appreciated!