I really like F#'s async workflow, but as for me, it has one serious issue: it does not allow creating workflows which should execute no longer than some specific timespan.
To make it clearer, here's a simple function I wrote for myself:
let withTimeout operation timeout = async {
try
return Some <| Async.RunSynchronously (operation, timeout)
with :? TimeoutException -> return None
}
I.e. signature is
val withTimeout : operation:Async<'a> -> timeout:int -> Async<'a option>
Example usage here:
let op = async {
do! Async.Sleep(1000)
return 1
}
#time
withTimeout op 2000 |> Async.RunSynchronously;;
// Real: 00:00:01.116, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0
// val it : unit option = Some 1
withTimeout op 2000 |> Async.RunSynchronously;;
// Real: 00:00:01.004, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
// val it : unit option = Some 1
withTimeout op 500 |> Async.RunSynchronously;;
// Real: 00:00:00.569, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
// val it : unit option = None
You can see, it works as expected. And it's very nice, but it is also a bit awkward and I'm not sure of it's safety and other issues which might arise. Maybe I am reinventing the wheel, and there's nice and concise way to write such workflows?
Async.RunSynchronously, you block the current thread and lose efficiency if underlying async operation is I/O bound. I think I haven't seen a working implementation in the wild yet, but I remember using an ugly hack to do this by converting async comps to observables and then merging them.RunSynchronouslyinto anotherasync. I guess that will allow to keep it concurrent. But that's ugly, I agree. I am more concerned with what @Lee said - that I don't actually kill the running task. But at the moment I have no idea how to fix that nicely