Async, Await, Future, Completer, in Dart - Succinct guide with examples
Tags: dart, beginners, tutorial
Published: 2023-01-30 14:41:41

# Async 

### Definition and use 
* keyword when defining a function that makes that function _asynchronous_. 

e.g.:
```dart
void myFunc() async { ... };
```

* When an `async` function is called, lines will be executed in a _synchronous_ fashion until the keyword `await` (explained below) is encountered, at which point the interpreter will "wait" until that line "resolves" (described below). 
* In the meantime, it will treat the function as "paused" (layman's description) and continue to run the next part of the program -- it will go back to the context of where the function is called and run the next line there.


### Rationale 
* If you want a function to be able to "pause" and "wait" for certain lines within it to resolve, because they aren't instantaneously available, the function must be `async`


# Await 

### Definition and use
* Within `async` functions, the keyword `await` can "pause" the interpreter on that line until it is "resolved". 
* Only things that return a `Future` object (explained below) can be `awaited`

```dart
void parentFunc() async {
  print("parent func start");
  // At the next line, the interpreter will "stop" and "wait"
  // until the thing that is being awaited (Future.delayed, 
  // explained below) is "resolved". 
  await Future.delayed(Duration(seconds: 3), ()=> print("parent func wait is finished")); 
  firstChildFunc();
  print('parent func continuing');
  secondChildFunc();
  print("parent func end");
  }
``` 

### Rationale
* Don't want to block other things from happening while one thing is being resolved, but do want to respect the order of events within a given function.
* Therefore, if something is taking time, "pause" there, keep calculating other things below where that function is called, and once that thing is resolved, keep going through that function.

### Examples 


Here is an exercise in trying to guess the order of print statements. There are more such exercises below. Write down which order you think it will print before scrolling further.

We have 
* a `main` func which is called when you run the program
* an async `parentFunc` which is referenced in the main func
* the `parentFunc` has two functions referenced in it. Both of those are `await` -ed.
* each child function is also async, and contains some await clauses

```dart
import 'dart:async';

void main() {
  parentFunc();
  print("main func complete");
}

void parentFunc() async {
  print("parent func start");
  await Future.delayed(Duration(seconds: 3), ()=> print("parent func wait is finished"));
  firstChildFunc();
  print('parent func continuing');
  secondChildFunc();
  print("parent func end");
  }

void firstChildFunc() async {
  print("1st child func start");
  await Future.delayed(Duration(seconds: 3), ()=> print("1st child first wait is finished"));
  await Future.delayed(Duration(seconds: 3), ()=> print("1st child second wait is finished"));
  print("1st child func end");
}

void secondChildFunc() async {
  print("2nd child func start");
  await Future.delayed(Duration(seconds: 3), ()=> print("2nd child first wait is finished"));
  print("2nd child func end");
}
```
.
.
.
.
.
.
Answer:
```dart

// parent func start
// main func complete
// parent func wait is finished
// 1st child func start
// parent func continuing
// 2nd func start
// parent func end
// 1st child first wait is finished
// 2nd child first wait is finished
// 2nd func end
// 1st child second wait is finished
// 1st func end

```

Explanation:
* the main function calls the parent function
* the parent function has a synchronous print statement, which, as expected, prints right away
* then we encounter an `await` which blocks `parentFunc()` from executing more lines, but unblocks the `main` function, hence the `main func complete`
* then we are solely blocked by the `parentFunc` 's `await` -- there is nothing remaining to execute in the `main` func
* we enter the `firstChildFunc` and print a statement immediately, then hit an `await`. So, as above, the `parentFunc` keeps running, hence:

```dart
// 1st child func start
// parent func continuing
// 2nd func start
// parent func end
```

* and so on.. 


# Future 

### Definition and use
* A future is an object that represents the result of an asynchronous operation. A future object will return a value at a later time.
* A future has two states: uncompleted and completed. When a future completes, it has two possibilities:
  * Completed with a value
  * Failed with an error


### Rationale
* Give a function the type `Future<T>` (where `T` is the type of what is resolved) so that it can be `await`-ed. 
* Otherwise you would have to type all the detail of what is being awaited into the `async` function that has the `await` keyword on one of its lines
* Not hard, just the object type that can be `await` -end

### Examples
* Below, the `main` function is `async`, therefore we can write `await` 
* The function that is being `await` -ed must be of type `Future<String>`
* That function returns a `Future<String>` object 
* Execute it. Nothing for two seconds, then the statement `Finally!` is printed to the console 

```dart
void main() async {
  String bigAsk = await slowlyGetTheString();
  print(bigAsk);
}

Future<String> slowlyGetTheString() {
  return Future.delayed(Duration(seconds: 2), () => "Finally!");
}
```


# Completer 

### Definition and use
* An object which can return be returned as a `Future`.
* Use when you want to define for yourself exactly what will be returned as the future.
* Initialise as a `Completer<T>()` (where `T` is the type that the future will ultimately resolve into 
* Fill with `completer.complete( ... )` (where `...` is of type `T`)
* Return as `completer.future`

### Rationale
* Have precise control over the type and contents of what is returned from a `Future` function.

### Examples

* Say I don't want to return exactly that which was retrieved from an API or a database, but instead I want to augment it slightly before returning it. 
* The completer lets me pack whatever I like into the `Future` object and then return that instead.
* Below, I call a function which then (pretends to) query an API (`firstPartOfPhrase`), then uses that string in a larger string, and returns _that_ as the future object, not the original (pretend) query: 

```dart
void main() async {
  String bigAsk = await slowlyGetTheString();
  print(bigAsk);
}

// Note, this is async only because I am using await, 
// not to do with completer
Future<String> slowlyGetTheString() async {

  // Initialise my completer
  Completer<String> c = Completer<String>();

  // Do something that takes time 
  String firstPartOfPhrase = await Future.delayed(Duration(seconds: 2), () => "Finally!");

  // Assign a string to the completer. Not necessarily the same 
  // string as was received from the line previously 
  c.complete("$firstPartOfPhrase it is finished!");

  // Now that the completer has that information, return completer.future 
  return c.future;
}
```