Ignoring Operation Results while using F# Async Computation Expressions

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:
  1. let renderHtml = async {  
  2.     BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision) |> Async.AwaitTask |> ignore  
  3.     let options = LaunchOptions()  
  4.     options.Headless <- true  
  5.     let! browser = Puppeteer.LaunchAsync(options) |> Async.AwaitTask  
  6.     let! page = browser.NewPageAsync() |> Async.AwaitTask  
  7.     page.GoToAsync("https://i.ua") |> Async.AwaitTask |> ignore      
  8.     return! page.GetContentAsync() |> Async.AwaitTask      
  9. }  
Since we actually don't care about download browser result, we naturally would expect our line to look like:
  1. BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision) |> Async.AwaitTask |> ignore  

This would be equivalent to the following C# code:

  1. await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);  
However, when we execute the following method, we get 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... 
  1. 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:

  1. let renderHtml = async {  
  2.     let! _ = BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision) |> Async.AwaitTask  
  3.     let options = LaunchOptions()  
  4.     options.Headless <- true  
  5.     let! browser = Puppeteer.LaunchAsync(options) |> Async.AwaitTask  
  6.     let! page = browser.NewPageAsync() |> Async.AwaitTask  
  7.     let! _ = page.GoToAsync("https://i.ua") |> Async.AwaitTask      
  8.     return! page.GetContentAsync() |> Async.AwaitTask      
  9. }  
Note how we use an underscore to show that the variable value is ignored.