Asynchronous workflow
Purpose
Do not block the current thread while waiting for a long calculation
Allow parallel calculations
Indicate that a calculation may take some time
Async<'T> type
Async<'T> typeRepresents an asynchronous calculation
π Similar to the async/await pattern, way before Cβ― and JS
2007:
Async<'T>Fβ―2012:
Task<T>.NET and patternasync/await2017:
PromiseJavaScript and patternasync/await
Methods returning an Async object
Async objectAsync.AwaitTask(task : Task or Task<'T>) : Async<'T>
β Convert a Task (.NET) to Async (Fβ―)
Async.Sleep(milliseconds or TimeSpan) : Async<unit>
β await Task.Delay() β Thread.Sleep β does not block current thread
FSharp.Control CommonExtensions module: extends the System.IO.Stream type (doc)
β AsyncRead(buffer: byte[], ?offset: int, ?count: int) : Async<int>
β AsyncWrite(buffer: byte[], ?offset: int, ?count: int) : Async<unit>
FSharp.Control WebExtensions module: extends type System.Net.WebClient (doc)
β AsyncDownloadData(address : Uri) : Async<byte[]>
β AsyncDownloadString(address : Uri) : Async<string>
Run an async calculation
Async.RunSynchronously(calc: Async<'T>, ?timeoutMs: int, ?cancellationToken) : 'T
β Waits for the calculation to end, blocking the calling thread! (β await Cβ―) β οΈ
Async.Start(operation: Async<unit>, ?cancellationToken) : unit
β Performs the operation in the background (without blocking the calling thread)
β οΈ If an exception occurs, it is "swallowed"!
Async.StartImmediate(calc: Async<'T>, ?cancellationToken) : unit
β Performs the calculation in the calling thread!
Async.StartWithContinuations(calc, continuations..., ?cancellationToken)
β Like Async.RunSynchronously β οΈ ... with 3 continuation callbacks:
β on success β
, exception π₯ and cancellation π
async { expression } block
async { expression } blockA.k.a. Async workflow
Syntax for sequentially writing an asynchronous calculation
β The result of the calculation is wrapped in an Async object
Keywords
returnβ final value of calculation β’unitif omittedlet!β access to the result of an async sub-calculation (βawaitin Cβ―)use!β likeuse(management of anIDisposable) +let!do!β likelet!for async calculation without return (Async<unit>)
Fβ― async function vs Cβ― async method
Let's compare an Fβ― async function...
... with its equivalent Cβ― async method:
We can see the following equivalence regarding keywords and types:

Async<int> type
Task<int> type
async {...} block
async method keyword
let! keyword
var and await keywords
do! keyword
await keyword
Inappropriate use of Async.RunSynchronously
Async.RunSynchronouslyAsync.RunSynchronously runs the calculation and returns the result BUT blocks the calling thread! Use it only at the "end of the chain" and not to unwrap intermediate asynchronous calculations! Use an async block instead.
Parallel calculations/operations
Async.Parallel
Async.Parallel(computations: Async<'T> seq, ?maxDegreeOfParallelism) : Async<'T array>
β Task.WhenAll : Fork-Join model
Fork: calculations run in parallel
Use the optional
maxDegreeOfParallelismto throttle/limit the number of concurrently executing tasks ; it's likeWithDegreeOfParallelism(throttle)in LINQ andParallelEnumerable.
Wait for all calculations to finish
Join: aggregation of results
In the same order as calculations
β οΈ All calculations must return the same type!
Async.StartChild
Async.StartChild(calc: Async<'T>, ?timeoutMs: int) : Async<Async<'T>>
Allows several operations to be run in parallel
β ... whose results are of different types (β Async.Parallel)
Used in async block with 2 let! per child calculation (cf. Async<Async<'T>>)
Shared cancellation π β Child calculation shares cancellation token with its parent calculation
Example:
We will test the following script in the console FSI, using the #time FSI directive (doc).
Let's first define a function delay
β which returns the specified value x
β after ms milliseconds
Timing results:
inSeries
00:00:00.323
00:00:00.015
inParallel
00:00:00.218
00:00:00.031
β inParallel is working, longing ~200ms vs ~300ms for inSeries.
β inParallel uses 2 times more CPU than inSeries.
Cancelling a task
Based on a default or explicit CancellationToken/Source:
Async.RunSynchronously(computation, ?timeout, ?cancellationToken)Async.Start(computation, ?cancellationToken)
Trigger cancellation
Explicit token +
cancellationTokenSource.Cancel()Explicit token with timeout
new CancellationTokenSource(timeout)Default token:
Async.CancelDefaultToken()βOperationCanceledExceptionπ£
Check cancellation
Implicit: at each keyword in async block:
let,let!,for...Explicit local:
let! ct = Async.CancellationTokenthenct.IsCancellationRequested.Explicit global:
Async.OnCancel(callback)
Example:
Summary
Adapted from π Cancellation Tokens in F#, by AndrΓ© Silva
CT = Cancellation Token CTS = Cancellation Token Source
do!, let!... in async {}
Any?
Async.StartChild
Parallel operations
Async.Start
Fire & forget: send a message to a bus...
Async.StartImmediately
?
Async.RunSynchronously
Program root, scripting...
Last updated
Was this helpful?