Let client be an instance of System.Net.Http.HttpClient. In the following code
var response = await client.PostAsync(url, content);
processResponse(response);
there is no thread block between the first and the second line of code, so if we are in the UI thread, the UI remains responsive during the POST round-trip.
What is the F# code to obtain the same non-blocking behaviour? Is
let response = client.PostAsync(url, content) |> Async.AwaitTask |> Async.RunSynchronously
processResponse(response)
the correct code? I haven't clear whether RunSynchronously is blocking the current thread. If so, how do we obtain the same non-blocking behaviour as await?
EDIT
Maybe a little more context would help.
I have a Visual Studio solution with 2 projects: a WPF/WinForms app and a F# library, referenced by the app. The library provides a function/method, named FSLongWork(), which executes a long I/O operation, e.g. an HTTP GET or POST to a remote server using HttpClient.GetAsync/PostAsync, and returns a string. The app front-end is a simple window with a button and a label. The button click handler must: 1) Call FSLongWork() in the F# library 2) Write in the Label a content that depends on the string returned in step 1. Of course step 1 must occur asynchronously, to preserve UI responsiveness.
POSSIBLE C# app SOLUTION
F# library:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
} |> Async.StartAsTask
C# app click handler
private async void button1_Click(object sender, EventArgs e) {
var s = await FSLongWork();
label1.Text = s;
}
C# app button handler registration
this.button1.Click += new System.EventHandler(this.button1_Click);
POSSIBLE F# app SOLUTION
F# library:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
}
F# app click handler
let button1_Click (sender : obj) e =
async {
let! s = FSLongWork()
label1.Text <- s
}
F# app button handler registration
button1.Click.Add(RoutedEventHandler(fun sender e -> button1_Click sender e |> Async.StartImmediate)
The problem I see is that the F# library function (FSLongWork) is different in the two solutions (|> Async.StartAsTask is only in the first), which is not good in term of reusability.
We can use the first implementation in F# (change let! s = FSLongWork()
to let! s = FSLongWork() |> Async.AwaitTask
).
And the second implementation can be used in C# (change var s = await FSLongWork();
to var s2 = await Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null);
).
Yet it looks a bit awkward to me:
the natural F# implementation would be the second (without Async.StartAsTask
), but this requires to reference Microsoft.FSharp
and the use of the rather ugly Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null);
in the C# app.
On the other hand, the first implementation (with Async.StartAsTask
), leads to a more natural use in C# (simply await FSLongWork()
), but implies am async->Task->async round-trip when used by a F# app
Is there a way to write the F# library so that a C# user doesn't need to reference FSharp.Core and without influencing how the F# function is implemented?