Reading — step 1 of 5
Learn
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
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
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…