Problem, short:
In an async{...} expression, in a try-finally expression, how can I use do! ... |> Async.AwaitTask in the finally block?
Problem, long; failing snippet:
I fail to aggregate all exceptions when awaiting a chain of nested asynchronous tasks.
My design intention is that each task eventually awaits its predecessing task, and I expect the Task-Parallel-Library uses the AggregateException to combine all exceptions observed.
Each async/task may throw an exception; the following minimal example chains/nests two asynchronous tasks, each throwing an exception:
async {
let actionBefore =
async {
printfn "doing sth before..."
failwith "! fail during sth before"
}
|> Async.StartAsTask
printfn "doing sth now..."
failwith "! fail during sth now"
do! actionBefore |> Async.AwaitTask
}
|> Async.StartAsTask
|> Async.AwaitTask
|> Async.RunSynchronously
My question is, without changing the starting and awaiting order, how do I have to add try-catch- or try-finally-blocks so that both exceptions will be reported by the final Async.AwaitTask?
I am using F# version 5.
The existing answer shows the key trick, which is to use Task.WhenAll - unlike other options, this waits for all the tasks and aggregates all exceptions. Just to add to this, I think it's confusing that you have to mix tasks and asyncs here. The plain F# way of doing this is to use Async.StartChild, but this fails whenever the first computation fails:
async {
let! t1 = Async.StartChild <| async {
printfn "doing sth before..."
failwith "! fail during sth before" }
let! t2 = Async.StartChild <| async {
printfn "doing sth now..."
failwith "! fail during sth now" }
let! r1 = t1
let! r2 = t2
return r1, r2 }
|> Async.RunSynchronously
There is no Async.WhenAll, but you can define that using Task.WhenAll. Here is a simpler version with just two arguments:
module Async =
let WhenBoth(a1, a2) = async {
let r1 = a1 |> Async.StartAsTask
let r2 = a2 |> Async.StartAsTask
let! _ = System.Threading.Tasks.Task.WhenAll(r1, r2) |> Async.AwaitTask
return r1.Result, r2.Result }
That way, you can write what you originally wanted in a fairly clean way, just using async:
async {
let! t1 = Async.StartChild <| async {
printfn "doing sth before..."
failwith "! fail during sth before" }
let! t2 = Async.StartChild <| async {
printfn "doing sth now..."
failwith "! fail during sth now" }
return! Async.WhenBoth(t1, t2) }
|> Async.RunSynchronously
I'm not an expert in async/task, but I think the main problem here is that do! actionBefore |> Async.AwaitTask can never execute, so there's no way to catch its exception.
Instead, I suggest that you start each action as a separate task, and then use Task.WhenAll to await both of them:
async {
let actionBefore =
async {
printfn "doing sth before..."
failwith "! fail during sth before"
}
|> Async.StartAsTask
let actionNow =
async {
printfn "doing sth now..."
failwith "! fail during sth now"
}
|> Async.StartAsTask
do! Task.WhenAll(actionBefore, actionNow)
|> Async.AwaitTask
|> Async.Ignore
}
|> Async.StartAsTask
|> Async.AwaitTask
|> Async.RunSynchronously
This results in an AggregateException that contains two inner exceptions, which is what I think you want.
Note that you don't need the outer async in this case, so I think the following version is simpler:
let actionBefore =
async {
printfn "doing sth before..."
failwith "! fail during sth before"
}
|> Async.StartAsTask
let actionNow =
async {
printfn "doing sth now..."
failwith "! fail during sth now"
}
|> Async.StartAsTask
Task.WhenAll(actionBefore, actionNow)
|> Async.AwaitTask
|> Async.RunSynchronously
|> ignore
Related
To simplify my scenario, let's suppose I have this simple code:
let someCondition = false
let SomeFuncThatThrows () =
async {
if someCondition then
raise <| InvalidOperationException()
return 0
}
let DoSomethingWithFoo (foo: int) =
Console.WriteLine (foo.ToString())
let SomeWrapper () =
async {
let! foo = SomeFuncThatThrows()
DoSomethingWithFoo foo
}
[<EntryPoint>]
let main argv =
Async.RunSynchronously (SomeWrapper ())
0
When executing it, it obviously just prints "0". However, some day, circumstances change, and some external factor makes someCondition become true. To prevent the program to crash in this scenario, I want to handle the exception. Then for an F# newbie it's easy to change SomeWrapper adding a try-with block, which most people would think that works:
let SomeWrapper () =
async {
let! foo =
try
SomeFuncThatThrows()
with
| :? InvalidOperationException ->
Console.Error.WriteLine "aborted"
Environment.Exit 1
failwith "unreachable"
DoSomethingWithFoo foo
}
However, this above doesn't work (the exception is still unhandled), because SomeFuncThatThrows returns a successful result: an Async<int> element. What throws an exception is the let! foo = bit because it awaits the async workload.
However, if you want to change SomeWrapper to fix the exception handling, many may think this is possible:
let SomeWrapper () =
async {
let foo =
try
let! fooAux = SomeFuncThatThrows()
fooAux
with
| :? InvalidOperationException ->
Console.Error.WriteLine "aborted"
Environment.Exit 1
failwith "unreachable"
DoSomethingWithFoo foo
}
But no, the compiler is not happy, as it signals the following error:
/.../Program.fs(17,17): Error FS0750: This construct may only be used
within computation expressions (FS0750) (SomeProject)
Then, it seems the only way I could fix it is this way:
let SomeWrapper () =
async {
try
let! foo = SomeFuncThatThrows()
DoSomethingWithFoo foo
with
| :? InvalidOperationException ->
Console.Error.WriteLine "aborted"
Environment.Exit 1
failwith "unreachable"
}
However, I'm not 100% happy with this solution, because the try-with is too wide, as it also covers the call to DoSomethingWithFoo function, which I wanted to leave outside the try-with block. Any better way to fix this without writing non-idiomatic F#? Should I report the compiler error as a feature-request in Microsoft's F# GitHub repo?
You can wrap the call to SomeFuncThatThrows in a new async that contains a try...with:
let SomeWrapper () =
async {
let! foo =
async {
try
return! SomeFuncThatThrows()
with
| :? InvalidOperationException ->
Console.Error.WriteLine "aborted"
Environment.Exit 1
return failwith "unreachable"
}
DoSomethingWithFoo foo
}
The answer from #nilekirk works and encodes directly the logic that you were looking for, but as you noted in the comments, it is a fairly complex syntactic structure - you need a nested async { .. } expression.
You could extract the nested async block into a separate function, which makes the code much more readable:
let SafeSomeFunc () = async {
try
return! SomeFuncThatThrows()
with
| :? InvalidOperationException ->
Console.Error.WriteLine "aborted"
Environment.Exit 1
return failwith "unreachable"
}
let SomeWrapper2 () = async {
let! foo = SafeSomeFunc ()
DoSomethingWithFoo foo
}
Here, we actually need to put some return value into the with branch.
Any better way to fix this without writing non-idiomatic F#?
In idiomatic F# and functional code, we try to get rid of using exceptions and side-effects as much as possible.
Environment.Exit is a big side-effect, don't use it.
If SomeFuncThatThrows() must be able to throw exception (because e.g., you cannot modify its source code). Then try to wrap it inside a safe function which returns an Option value and use this function instead.
Your whole code can be rewritten as:
let someCondition = true
let SomeFuncThatThrows () =
async {
if someCondition then
raise <| InvalidOperationException()
return 0
}
let SomeFunc () =
async {
try
let! foo = SomeFuncThatThrows()
return Some foo
with _ ->
return None
}
let DoSomethingWithFoo (foo: int) =
Console.WriteLine (foo.ToString())
let SomeWrapper () =
async {
match! SomeFunc() with
| Some foo -> DoSomethingWithFoo foo
| None -> Console.Error.WriteLine "aborted"
}
[<EntryPoint>]
let main argv =
Async.RunSynchronously (SomeWrapper ())
0
I would like to block async execution until the users presses any key in the console.
Here is what I have come up with:
let waitForAnyKey = async {
do!
new Task (fun () ->
printfn "%s" "waiting for a key"
Console.ReadKey () |> ignore
printfn "%s" "got a key"
)
|> Async.AwaitTask
}
Used like this:
async {
printfn "%s" "Press any key... "
do! waitForAnyKey
printfn "%s" "You pressed a key. "
}
However, the Task is never run.
How should I write this in F#?
Can I avoid creating a Task for this entirely?
Without tasks, with Async.FromContinuations
open System
let waitForAnyKey =
Async.FromContinuations (fun (cont, _, _) ->
printfn "%s" "waiting for a key"
cont (Console.ReadKey ())
printfn "%s" "got a key"
)
let test () =
async {
let! key = waitForAnyKey
printfn "%O" key.Key
}
|> Async.RunSynchronously
test ()
You can write waitForAnyKey without the Task<->Async interop bits:
let waitForAnyKey = async {
do Console.ReadKey () |> ignore
}
You can then call it any time you'd like to wait for input. Your usage example would work just fine with this implementation.
The Task constructor creates a new Task but does not start it. Using Task.Run instead should fix the problem:
let waitForAnyKey = async {
do!
Task.Run (fun () ->
printfn "%s" "waiting for a key"
Console.ReadKey () |> ignore
printfn "%s" "got a key"
)
|> Async.AwaitTask
}
Console.ReadKey does not currently have an equivalent that returns a Task (which surprised me), although the idea has been suggested.
I am running a simple chat app with f#. In the chat when one user types "exit" then I want both clients to finish the chat. Currently I am running in the console, and so read and write are blocking, but I am using a class to wrap the console so there is no async problems.
(In the following code the sendUI and reciveUI are async functions that send and recieve messages over the wire)
type IConnection =
abstract Send : string -> Async<bool>
abstract Recieve : unit -> Async<string>
abstract Connected : bool
abstract Close : unit -> unit
type IOutput =
abstract ClearLine : unit -> unit
abstract ReadLine : ?erase:bool -> string
abstract WriteLine : string -> unit
let sendUI (outputer:#IOutput) (tcpConn: #IConnection) () =
async {
if not tcpConn.Connected then return false
else
let message = outputer.ReadLine(true)
try
match message with
| "exit" -> do! tcpConn.Send "exit" |> Async.Ignore
return false
| _ -> if message.Trim() <> ""
then do! message.Trim() |> tcpConn.Send |> Async.Ignore
outputer.WriteLine("me: " + message)
return true
with
| e -> outputer.WriteLine("log: " + e.Message)
return false
}
let recieveUI (outputer:#IOutput) (tcpConn: #IConnection) () =
async {
if not tcpConn.Connected then return false
else
try
let! response = tcpConn.Recieve()
match response with
| "exit" -> return false
| _ -> outputer.WriteLine("other: " + response)
return true
with
| e -> outputer.WriteLine("error: " + e.Message)
return false
}
let rec loop (cancel:CancellationTokenSource) f =
async {
match! f() with
| false -> cancel.Cancel(true)
| true -> do! loop cancel f
}
let messaging recieve send (outputer: #IOutput) (tcpConn:#IConnection) =
printfn "write: exit to exit"
use cancelSrc = new CancellationTokenSource()
let task =
[ recieve outputer tcpConn
send outputer tcpConn ]
|> List.map (loop cancelSrc)
|> Async.Parallel
|> Async.Ignore
try
Async.RunSynchronously (computation=task, cancellationToken=cancelSrc.Token)
with
| :? OperationCanceledException ->
tcpConn.Close()
let exampleReceive =
{ new IConnection with
member this.Connected = true
member this.Recieve() = async { do! Async.Sleep 1000
return "exit" }
member this.Send(arg1) = async { return true }
member this.Close() = ()
}
let exampleOutputer =
{ new IOutput with
member this.ClearLine() = raise (System.NotImplementedException())
member this.ReadLine(erase) = Console.ReadLine()
member this.WriteLine(arg) = Console.WriteLine(arg) }
[<EntryPoint>]
let main args =
messaging recieveUI sendUI exampleOutputer exampleReceive
0
(I wrapped the console with an object so i wont get weird things on screen: outputer)
When I get "exit" over the wire i return false and so the loop calls cancel so it should also stop the sending messages async computation.
However, when I do this, the sendUI gets stuck:
async {
//do stuff
let message = Console.ReadLine() //BLOCKS! doesn't cancel
//do stuff
}
One fix would be to somehow make Console.ReadLine() an async, however the simple async { return ...} does not work.
I also tried running it as a task and calling Async.AwaitTask, but this does not work either!
I read that one can use Async.FromContinuations but I couldn't figure out how to use it (and what I tried didn't solve it...)
Little help?
EDIT
The reason this doesn't simply work is because the way async computations cancellation work. They check whether to cancel when it reaches a let!/do!/return! etc, and so the solutions above do not work.
EDIT 2
Added runnable code sample
You can wrap the Console.ReadLine in its own async, then call that with Async.RunSynchronously and a CancellationToken. This will allow you to cancel that blocking operation, because it won't be on the same thread as the console itself.
open System
open System.Threading
type ITcpConnection =
abstract member Send: string -> unit
let readLineAsync cancellation =
async {
try
return Some <| Async.RunSynchronously(async { return Console.ReadLine() }, cancellationToken = cancellation)
with | _ ->
return None
}
let receiveUI cancellation (tcpConnection: ITcpConnection) =
let rec loop () =
async {
let! message = readLineAsync cancellation
match message with
| Some msg -> msg |> tcpConnection.Send
| None -> printfn "Chat Session Ended"
return! loop ()
}
loop () |> Async.Start
Is there a difference between writing something like this:
MailboxProcessor.Start(fun inbox -> async {
let rec loop bugs =
let! msg = inbox.Receive()
let res = //something
loop res
loop []})
And writing it like this:
MailboxProcessor.Start(fun inbox ->
let rec loop bugs = async {
let! msg = inbox.Receive()
let res = //something
do! loop res }
loop [])
Thanks!
The first example is not valid F# code, because let! can only be used immediately inside computation expression. In your example, you're using it in an ordinary function - its body is not a computation expression, so let! is not allowed in that position.
To make it valid, you'd need to wrap the body of the loop function inside async:
MailboxProcessor.Start(fun inbox -> async {
let rec loop bugs = async {
let! msg = inbox.Receive()
let res = //something
return! loop res }
return! loop []})
You can keep the outer async { .. } block in the snippet as well - then you just need to use return! to call your loop function instead of just returning it (but other than that there is no significant difference now).
Note that I used return! instead of do! - this actually makes a difference, because return! represents a tail-call, which means that the rest of the current body can be discarded. If you use do! then the async allocates something like a stack frame in the heap, so using do! in a recursive looping function leaks memory.
I'm a bit frustrated here. I know I've got all the bits, but I can't work out how to combine them...
let saveImageToDisk path content =
async {
use s = new FileStream(path, FileMode.OpenOrCreate)
do! s.AsyncWrite(content)
printfn "Done writing %A" path
} // returns Async<unit>
let getImages imageUrls =
imageUrls
|> Seq.map (fun url -> topath url, getImage url)
//Next line not happy because content is Async<byte[]> instead of byte[]
|> Seq.map (fun (path, content) -> saveImageToDisk path content)
|> Async.Parallel
|> Async.RunSynchronously
You can combine the two using the async expression:
let getImages imageUrls =
imageUrls
|> Seq.map (fun url -> async {
let! content = getImage url
return! saveImageToDisk (topath url) content })
|> Async.Parallel
|> Async.RunSynchronously