Concurrency, 6 of 8
Async and Await
Write code that waits for files and other work without blocking, with Async Functions, Await, and the errors their Tasks carry.
What you’ll build
A function that reads a file without blocking, hands the counting to a worker thread, and gives a one-line summary. The program starts it for two files at once, one of which doesn’t exist, and handles the error that Task carries.
An Async Function
Async Function marks a function that can wait. Calling one runs it until its first Await, and then
gives its Task<T>. Inside it, Await t waits for a Task: the function pauses there, the main
thread goes on to other work, and the function resumes on the main thread when the Task is done.
one
two
threeImport std.core
Import std.io
Import std.text
Function CountVowels(text: String): Int
Local count = 0
For c In text.Chars
If "aeiou".Contains(c.ToString()) Then count += 1
End For
Return count
End Function
Async Function Summarize(path: String): String Throws IOError
Const text = Try Await File.ReadTextAsync(path)
Local vowels = Await Spawn(Function() => CountVowels(text))
Return "{path}: {text.SplitLines().Count} lines, {vowels} vowels"
End Function
Local first = Summarize("notes.txt")
Local second = Summarize("missing.txt")
Print("both started")
Try
Print(Await first)
Print(Await second)
Catch e As IOError
Print("couldn't summarize: {e.Kind}")
End Tryboth started notes.txt: 3 lines, 5 vowels couldn't summarize: NOT_FOUND
Both calls start their reads before both started prints, so the two files are read at the same
time. File.ReadTextAsync is std.io’s Async form of File.ReadText. Await Spawn(...) sends the
counting to a worker thread and waits for it without blocking the main thread, so heavy work and
waiting mix freely. text is a Const, so the work on the other thread can use it.
Errors in a Task
An Async Function’s errors go in its Task, so calling it never throws. Await throws them, so an
Await of a Task that can fail needs Try, as a call that throws does. The Task’s type says what
it can throw: Summarize gives a Task<String> Throws IOError.
Import std.core
Import std.io
Async Function Load(path: String): String Throws IOError
Return Try Await File.ReadTextAsync(path)
End Function
Local text = Await Load("notes.txt")
Print(text) Compiling main.fb
main.fb:8:14: error: this Await can throw IOError, as its Task's Async Function can, so it needs Try, Try?, or a Try block
|
8 | Local text = Await Load("notes.txt")
| ^^^^^^^^^^^^^^^^^^^^^^^
error: couldn't build main.fb because of 1 errorIn the program, the second Await throws the IOError that reading missing.txt gave, and the
Catch handles it. Each Await of a Task that failed throws its error again.
Where Await goes
Await goes in Async Functions, Async Methods, and Async lambdas, and in top-level code. In a function that isn’t Async, it’s an error:
Import std.core
Import std.io
Function Load(path: String): String Throws IOError
Return Try Await File.ReadTextAsync(path)
End Function
Print("never built")Compiling main.fb main.fb:5:16: error: Await goes in an Async Function or lambda, whose code can wait without blocking its thread, or in top-level code | 5 | Return Try Await File.ReadTextAsync(path) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: couldn't build main.fb because of 1 error
An Await in top-level code waits as Wait() does, and while it waits, the main thread runs the
Async Functions that are ready to go on. Async code always runs on the main thread, so it shares the
program’s Globals and objects without locks; only the work given to Spawn, Thread.Start, and
Parallel For runs elsewhere.
std’s I/O has Async forms throughout: files, sockets, processes, and HTTP, like HttpClient’s
GetAsync. A Method can be Async too, and so can an Interface’s.
What you learned
- Calling an
Async Functionstarts it and gives aTask<T>. Awaitwaits for a Task without blocking the main thread, and gives its result.Await Spawn(...)runs heavy work on another core from Async code.- An Async Function’s errors go in its Task, and
Awaitthrows them, so it needsTry. - Async code runs on the main thread.
The rules are in the language reference.
Report an issue with this page or with FutureBlitz on GitHub.