Step 1 of 5 · Reading · ~2 min
Learn
Async, Type Providers, Interop
F#'s async { ... } is a computation expression (you saw the pattern). It lets you write asynchronous code that looks sequential.
Distinction: F# Async<'T> is cold (does nothing until started). C# Task<T> is hot (starts immediately when created). This matters for composition.
Basic async:
let fetchAsync (url: string) : Async<string> =
async {
use client = new System.Net.Http.HttpClient()
let! response = client.GetStringAsync(url) |> Async.AwaitTask
return response
}
let! unwraps an Async (like await in C#). use is using — disposes the client when done.
Running async:
let result = fetchAsync "https://example.com" |> Async.RunSynchronously
RunSynchronously blocks the current thread. Use it at the top level (e.g., main) only.
Parallel async:
let urls = ["https://a.com"; "https://b.com"; "https://c.com"]
let results =
urls
|> List.map fetchAsync // List<Async<string>>
|> Async.Parallel // Async<string[]> — run concurrently
|> Async.RunSynchronously // string[]
Async.Parallel schedules all of them concurrently, returns an array when all complete. Async.Sequential runs in order.
Cancellation:
let cts = new System.Threading.CancellationTokenSource()
Async.Start(longTask, cts.Token)
// ... later ...
cts.Cancel()
F# 6+ added task { ... } for direct C# Task interop — same syntax, hot semantics.
When to use which:
async {}for F#-idiomatic code, cold composition, cancellation controltask {}when interfacing with C# libraries that expect Tasks- Both for HTTP, file I/O, DB calls, anything I/O-bound
Order comes from the array, not from the printing. Async.Parallel returns results in the order of the workflows you handed it, however the scheduler happened to interleave them. So compute inside the workflow and print outside it:
let doubleAsync n = async { do! Async.Sleep 1; return n * 2 }
// WRONG — printfn inside the workflow runs in completion order:
// async { do! Async.Sleep 1; printfn "%d" (n * 2) }
// for [1;2;3] this really does print 4, 2, 6 on a bad day.
let results =
[1..3] |> List.map doubleAsync |> Async.Parallel |> Async.RunSynchronously
for r in results do printfn "%d" r
// 2
// 4
// 6
Anti-patterns:
Async.RunSynchronouslyinside another async — defeats the point- Forgetting to await —
let result = fetchAsync urlgives you the Async, not the value - Catching exceptions outside async — wrap in
try/withinside the workflow - Printing from inside a workflow you are about to run in parallel — the output arrives in completion order, and the ordering bug is intermittent, so it will pass on your machine and fail on the grader
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…