Tasks and .NET Async

Cold (explicit start) Hot (runs immediately) Better (state machines) F#-centric code, composition ASP.NET Core,.NET library interop

open System.Net.Http
open System.Threading.Tasks

let client = new HttpClient()

let getLength (url: string) : Task<int> = task {
    let! body = client.GetStringAsync url
    return body.Length
}

let main () = task {
    let! lengths =
        [ "https://example.com"; "https://dotnet.microsoft.com" ]
        |> List.map getLength
        |> Task.WhenAll                          // parallel
    for len in lengths do printfn "%d" len

    // use! for async disposables, try/with/finally inside tasks
    try
        use! response = client.GetAsync "https://example.com"
        response.EnsureSuccessStatusCode() |> ignore
    with ex -> eprintfn "Failed: %s" ex.Message
}

main().Wait()
// ValueTask: backgroundTask { } (runs on the thread pool), valueTask via IcedTasks
// Cancellation: pass CancellationToken explicitly to .NET APIs; IcedTasks provides cancellableTask { }