macosvisual-studiof#visual-studio-codeionide

Why don't I get output on OSX with this F# code but I do on Windows


I'm trying to execute the following F# script code on my macbook pro using FSI in visual studio code and the ionide plugin.

#r "packages/Newtonsoft.Json.9.0.1/lib/net40/Newtonsoft.Json.dll"
#r "System.Net.Http"

open System
open System.Net.Http
open Newtonsoft.Json

let client = new HttpClient()

type AlbumInfo = { userId:int; id:int; title:string }

let url = "https://jsonplaceholder.typicode.com/albums/1"

async {
    let! res = Async.AwaitTask <| client.GetAsync(url)
    let! content = Async.AwaitTask <| res.Content.ReadAsStringAsync()
    let  x = JsonConvert.DeserializeObject<AlbumInfo>(content)

    printfn "%s" x.title
} |> Async.Start

printfn "Please wait..."

But I don't get any output apart from Please wait.... However, when I put https://jsonplaceholder.typicode.com/albums/1 into the browser I get the expected Json response. So I know there's no problem reaching the API.

Also, when I run the same code in Visual Studio 2013 on my Windows 10 PC. The code produces the expected result. i.e. Please wait... and the title of the album.

Any ideas why it doesn't work correctly on my macbook?


Solution

  • In Visual Studio there is a process hosting FSI and keeping the thread (pool) for the async computation alive. In FSI on the command line or VS Code, FSI will just terminate as soon as the main thread has finished writing Please wait... (which typically is before the computation was even started on the thread pool).

    If you want to observe the side effects of an async computation you have to await its result (in this example unit):

    let computation = async {
        printfn "Starting async"
        let! res = Async.AwaitTask <| client.GetAsync(url)
        let! content = Async.AwaitTask <| res.Content.ReadAsStringAsync()
        let x = JsonConvert.DeserializeObject<AlbumInfo>(content)
        printfn "Downloaded %s" x.title
    }
    
    async {
        let! started = computation |> Async.StartChild
        let! _ = Async.Sleep 1 // only here to get interleaved ouput
        printfn "Please wait..."
        let! res = started
        printfn "Got result %A" res
    } |> Async.RunSynchronously
    

    will likely print:

    Starting async
    Please wait...
    Downloaded quidem molestiae enim
    Got result <null>