Async/await expresses asynchronous work in sequential-looking syntax: execution suspends while a result is produced, avoiding the UI-blocking and composition problems of older callback-oriented code. The pattern became widely popular with C# 5 and now appears across many modern languages. section 12.1.2
Figure 12.1 Comparing synchronous and asynchronous flows. The synchronous flow follows a single, sequential flow. The asynchronous flow branches into two streams, which requires the ability to coordinate the two streams back into one at some point in the future.
Compare the single sequential flow of synchronous work with the branching and later coordination required by asynchronous work. section 12.1.2
F# reaches the same goal with a related but different model. Use task { } and async { } blocks, then await Task and Async values with let! and do!. Task is the general-purpose starting point; Async remains important because the abstractions have different costs and benefits. section 12.2 section 12.4
Apply the pattern to workloads where waiting or extended computation should not unnecessarily hold up application progress:
- Handle long-running, I/O-heavy, or CPU-heavy workloads to improve application throughput. section 12.4
- Coordinate concurrent results with Task.WhenAll or Async.Parallel. section 12.4
- Expect asynchronous values to spread through application boundaries, a viral effect that can require deliberate techniques to reduce. section 12.4
Choosing the F# form
For a general-purpose asynchronous operation, begin with task { }, using let! or do! where a Task value must be awaited. Use async { } when working with the older Async abstraction or its surrounding patterns; both support asynchronous composition, but neither removes the need to reason about parallel work and long-running processes. section 12.2 section 12.4
















