DEV Community

machine-gurning
machine-gurning

Posted on

Async, Await, Future, Completer, in Dart - Succinct guide with examples

Async

Definition and use

  • keyword when defining a function that makes that function asynchronous.

e.g.:

void myFunc() async { ... };
Enter fullscreen mode Exit fullscreen mode
  • 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
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");
  }
Enter fullscreen mode Exit fullscreen mode

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
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");
}
Enter fullscreen mode Exit fullscreen mode

.
.
.
.
.
.
Answer:


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

Enter fullscreen mode Exit fullscreen mode

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:
// 1st child func start
// parent func continuing
// 2nd func start
// parent func end
Enter fullscreen mode Exit fullscreen mode
  • 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
void main() async {
  String bigAsk = await slowlyGetTheString();
  print(bigAsk);
}

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

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:
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;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)