Async
Definition and use
- keyword when defining a function that makes that function asynchronous.
e.g.:
void myFunc() async { ... };
- When an
async
function is called, lines will be executed in a synchronous fashion until the keywordawait
(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 keywordawait
can "pause" the interpreter on that line until it is "resolved". - Only things that return a
Future
object (explained below) can beawaited
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 areawait
-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");
}
.
.
.
.
.
.
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
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 blocksparentFunc()
from executing more lines, but unblocks themain
function, hence themain func complete
- then we are solely blocked by the
parentFunc
'sawait
-- there is nothing remaining to execute in themain
func - we enter the
firstChildFunc
and print a statement immediately, then hit anawait
. So, as above, theparentFunc
keeps running, hence:
// 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>
(whereT
is the type of what is resolved) so that it can beawait
-ed. - Otherwise you would have to type all the detail of what is being awaited into the
async
function that has theawait
keyword on one of its lines - Not hard, just the object type that can be
await
-end
Examples
- Below, the
main
function isasync
, therefore we can writeawait
- The function that is being
await
-ed must be of typeFuture<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!");
}
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>()
(whereT
is the type that the future will ultimately resolve into - Fill with
completer.complete( ... )
(where...
is of typeT
) - 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;
}
Top comments (0)