Why is Async.RunSynchronously hanging? - f#

In my f# project, I'm calling a c# library that returns me a Task. When I try to do this in my f# project, nothing happens.
let str = getName() |> Async.AwaitTask |> Async.RunSynchronously
However, if I update my code to use an async workfolow, it doesn't hang anymore. What am I doing wrong when calling Async.RunSynchronously?
async {
let! str = getName() |> Async.AwaitTask
//Do something with str
}

In your second example you just build async workflow, but don't actually run it.
It is designed that way so you could define a complex workflow without running every async part of it immediately, but instead run the whole thing when it's good and ready.
In order to run it you need to call Async.RunSynchronously or Async.StartAsTask:
async {
let! str = getName() |> Async.AwaitTask
//Do something with str
} |> Async.RunSynchronously
Also, if you do need to work with Tasks, it's probably better to use TaskBuilder.fs instead of Async.

Related

Running async computation expression in FSI

I'm trying to run this code in the FSI
#r #"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Http.dll"
async {
let httpClient = new System.Net.Http.HttpClient()
let! response = httpClient.GetAsync("") |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
return! response.Content.ReadAsStringAsync() |> Async.AwaitTask
}
and I get the following error
Error FS0193: The module/namespace 'System.Threading.Tasks' from
compilation unit 'System.Runtime' did not contain the namespace,
module or type 'Task`1'
However when I paste that code in a file in a project it compiles as expected.
What I'm missing?
I'm not sure why you are getting this error, but you should be able to reference standard .NET libraries without specifying the full path.
I tried creating an F# Script with the following and it worked perfectly fine (I'm still using F# 5, but I do not expect this would change in a new version):
#r #"System.Net.Http.dll"
async {
let httpClient = new System.Net.Http.HttpClient()
let! response = httpClient.GetAsync("http://tomasp.net") |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
return! response.Content.ReadAsStringAsync() |> Async.AwaitTask
}
|> Async.RunSynchronously
I just had to add a real URL to download and Async.RunSynchronously to actually run the code.

How to write a Task.ContinueWith within an Async computation expression

I'm struggling to figure out how to write a Task.ContinueWith within a Async computation expression.
Ultimately, I want to process a cancel without relying on the Cancel Exception. As a result, I thought I could use Task.ContinueWith.
However, I'm in-over-my-head on trying to write this.
Any suggestions?
let rec receiveXmlMessage connection (cancellation:CancellationToken) queue =
async {
use receiveCommand = new SqlCommand(receiveQuery, connection, CommandTimeout = 0)
let result = receiveCommand.ExecuteNonQueryAsync(cancellation)
result.ContinueWith(fun (someResult:Task<int>) -> CancellableResult.Cancelled // IDK... ) |> AsyncResult.fromAsync
}
Error Type mismatch. Expecting a
Task> -> 'b but given a
Async<'c> -> Async> The type 'Task>' does not match the type 'Async<'b>'
Sorry, this isn't necessarily an answer but wouldn't fit well as a comment.
Do you need to pass a cancellation token to your workflow? I think it might be better to use Async.CancellationToken to propagate the workflow's existing token:
async {
let! ct = Async.CancellationToken // gets the async workflow's current cancellation token
use receiveCommand = new SqlCommand(receiveQuery, connection, CommandTimeout = 0)
let! result = receiveCommand.ExecuteNonQueryAsync(ct) |> Async.AwaitTask
// do something with the result
}
By threading the async workflow's cancellation token through to the computation you care about, you enable cancelling the workflow to do the right thing - it can cancel the top-level workflow when transitioning from one step to another, or it can cancel the query execution if that's what's executing.
Then when you schedule your async via a method like Async.RunSynchronously or Async.Start you can pass in an existing cancellation token if you have one.
However, it's not completely clear to me if this is what you're actually trying to do, since you're using let rec but you haven't indicated any need for recursive logic.

Running c# async method in f# workflow

I am trying to get the below code to work in a F# async workflow, but I am getting the error "Unexpected symbol '}' in expression". I am fairly new to both F# and async in general. What am I missing here.
let someFunction (req : HttpRequestMesssage) a b =
// code
async{
let! readToProvider =
req.Content.ReadAsMultipartAsync(provider)
|> Async.AwaitIAsyncResult
} |> Async.RunSynchronously
req.CreateResponse(HttpStatusCode.OK)
I worry that my previous answer wasn't quite what you want. What I supplied just got you through the compile error. But one thing about it, is that it does not run asynchronously. Task.Wait and Async.RunSynchronously will both block the running thread until the operation is complete.
If you want to actually be async, i.e. not blocking, you have to put the entire method, or at least the last part of it, into the async block, such that you're actually returning an async op to the caller. So the answer would be
let someFunction (req : HttpRequestMesssage) a b =
async {
let! readToProvider = (req.Content.ReadAsMultipartAsync provider) |> Async.AwaitIAsyncResult
return req.CreateResponse HttpStatusCode.OK
}
This option returns not the response, but an Async<Response>. So now the caller can decide how to run it, either blocking or truly asynchronously.
This way if you're using a web server that handles asynchronous requests, then you can simply connect this function to an endpoint (probably converting the Async to a Task at the point of connection, since most .net async web servers are written from C# perspective) and it'll run asynchronously without blocking a thread. Or if you're calling it from another async op you can do do! someFunction ... and it'll run asynchronously. But if the caller doesn't care and just wants to run synchronously, it can do someFunction ... |> Async.RunSynchronously. So you get more flexibility there. And you can always define let someFunctionSync ... = someFunction ... |> Async.RunSynchronously if that's the more common use case.
I'd recommend going this way unless you really want to enforce blocking.
You're doing it right. You're only getting the error because you're ending your async block with a let! expression. Change it to return!, or do! ... |> Async.Ignore and you'll be good.
Blocks in F# (neither workflows nor regular code blocks) should not end with let.
Of course if all you're really doing in the workflow is that one call, you don't need the workflow block at all (you never really should need to write a block for a single call). Just do
req.Content.ReadAsMultipartAsync provider
|> Async.AwaitIAsyncResult
|> Async.Ignore
|> Async.RunSynchronously
req.CreateResponse HttpStatusCode.OK
Or for that matter, just use the built in Tasks Wait, which does the same thing as Async.RunSynchronously:
(req.Content.ReadAsMultipartAsync provider).Wait()

F# Async.AwaitEvent and COM interop events

I have a COM object, which I connect to, and I should recieve an event, which would confirm that connection is established. I write code and test it in F# interactive, and for some reason it wouldn't catch COM events when I use Async.RunSynchronously.
/// This class wraps COM event into F# Async-compatible event
type EikonWatcher(eikon : EikonDesktopDataAPI) =
let changed = new Event<_>()
do eikon.add_OnStatusChanged (fun e -> changed.Trigger true)
member self.StatusChanged = changed.Publish
/// My method
let ``will that connection work?`` () =
let eikon = EikonDesktopDataAPIClass() :> EikonDesktopDataAPI // create COM object
let a = async {
let watcher = EikonWatcher eikon // wrap it
eikon.Initialize() |> ignore // send connection request
let! result = Async.AwaitEvent watcher.StatusChanged // waiting event
printfn "%A" result // printing result
return result
}
// I use either first or second line of code, not both of them
Async.Start (Async.Ignore a) // does not hang, result prints
Async.RunSynchronously (Async.Ignore) a // hangs!!!
/// Running
``will that connection work?`` ()
At the same time, code works perfectly well with RunSynchronously when I insert it into console app.
What should I do so that to prevent that nasty behavior?
The code we write under within a single Thread (as in STA) feels like it is made of independant pieces each having their own life, but this is actually a fallacy : everything is mediated under a common event loop, which "linearizes" the various calls.
So everything we do, unless explicitely spoecified otherwise, is essentially single threaded and you can not wait for yourself without creating a deadlock.
When you specify Async.Start it does start a new, independant computation which runs on its own, a "thread".
Whereas When you call runsynchronously, it awaits on the same 'thread'.
Now if the event you are waiting, which feels like an independant thing, is actually 'linearized' by the same event loop, you are actually waiting for yourself, hence the deadlock.
Something useful if you want to wait "asynchronously", (aka wait for an event, but not actually block and leave the opportunity for any other task to perform work) you can use the following code within your async block :
async {
....
let! token = myAsyncTask |> Async.StartChild
let! result = token
....
}

WebSharper force a message passing call to be asynchronous

Knowing an RPC call to a server method that returns unit is a message passing call, I want to force the call to be asynchronous and be able to fire the next server call only after the first one has gone to the server.
Server code:
[<Rpc>]
let FirstCall value =
printfn "%s" value
async.Zero()
[<Rpc>]
let SecondCall() =
"test"
Client code:
|>! OnClick (fun _ _ -> async {
do! Server.FirstCall "test"
do Server.SecondCall() |> ignore
} |> Async.Start)
This seems to crash on the client since returning unit, replacing the server and client code to:
[<Rpc>]
let FirstCall value =
printfn "%s" value
async { return () }
let! _ = Server.FirstCall "test"
Didn't fix the problem, while the following did:
[<Rpc>]
let FirstCall value =
printfn "%s" value
async { return "" }
let! _ = Server.FirstCall "test"
Is there another way to force a message passing call to be asynchronous instead?
This is most definitely a bug. I added it here:
https://bugs.intellifactory.com/websharper/show_bug.cgi?id=468
Your approach is completely legit. Your workaround is also probably the best for now, e.g. instead of returning Async<unit> return Async<int> with a zero and ignore it.
We are busy with preparing the 2.4 release due next week and the fix will make it there. Thanks!
Also, in 2.4 we'll be dropping synchronous calls, so you will have to use Async throughout for RPC, as discussed in https://bugs.intellifactory.com/websharper/show_bug.cgi?id=467 -- primarily motivated by new targets (Android and WP7) that do not support sync AJAX.

Resources