Introduction
This post explains a simple pitfall where C# developers trying out F# may fail when writing async code.
Consider this simple code in which we download page contents using Puppeteer-sharp:
- let renderHtml = async {
- BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision) |> Async.AwaitTask |> ignore
- let options = LaunchOptions()
- options.Headless <- true
- let! browser = Puppeteer.LaunchAsync(options) |> Async.AwaitTask
- let! page = browser.NewPageAsync() |> Async.AwaitTask
- page.GoToAsync("https://i.ua") |> Async.AwaitTask |> ignore
- return! page.GetContentAsync() |> Async.AwaitTask
- }
- BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision) |> Async.AwaitTask |> ignore
This would be equivalent to the following C# code:
- await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
AggregateException which in turn contains the following inner exception: "Chromium revision is not downloaded. Run BrowserFetcher.DownloadAsync or download Chromium manually". It seems like we've called the following... - let! browser = Puppeteer.LaunchAsync(options) |> Async.AwaitTask
...without waiting for the BrowserFetcher result.
And indeed, in order to await async call, we have to use let! as the construct. The code below works as expected:
- let renderHtml = async {
- let! _ = BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision) |> Async.AwaitTask
- let options = LaunchOptions()
- options.Headless <- true
- let! browser = Puppeteer.LaunchAsync(options) |> Async.AwaitTask
- let! page = browser.NewPageAsync() |> Async.AwaitTask
- let! _ = page.GoToAsync("https://i.ua") |> Async.AwaitTask
- return! page.GetContentAsync() |> Async.AwaitTask
- }

Join the conversation! Your thoughts help the community grow.