I do not understand how to use task builder right. How to get result correctly and elegant?
let t = task { return! someTaks() }
let r = t.Result // or
t.Wait()
let r = t.Result // or
let r = t |> Async.AwaiTask |> Async.RunSynchronously
// or any?
I do not understand how to use task builder right. How to get result correctly and elegant?
let t = task { return! someTaks() }
let r = t.Result // or
t.Wait()
let r = t.Result // or
let r = t |> Async.AwaiTask |> Async.RunSynchronously
// or any?
Whew that’s kind of a big topic!
For starters, there’s two different types for doing mostly the same thing: Async
and Task
, and some of the confusion I think stems from why both exist. In short, F# made Async
first, and then C# made Task
later and F# had to support Task
without getting rid of Async
. Typically F#-only solutions tend to use Async
, while solutions with both C# and F# mixed tend to use Task
instead.
Secondly, one should have some care with Async.RunSynchronously
, or any of the equivalents you’ve pointed out, since it effectively undoes all of the benefits of asynchronous programming. The official guidance is
- Async and Task Programming - F# | Microsoft Learn
If you’re not at the entry point for the executable, and you do care about performance, then there really isn’t a way to escape Task
/Async
. If you have some value t : Task<int>
and you have a function fn
that wants to use the int
value in t
,
let fn () =
t.Result + 5
then really you need to change fn
to make it asyncronous as well
let fn () = task {
let! value = t
return value + 5
}
This should be the default approach in your code to make sure you’re getting the benefits of asynchronous programming.
So with all that out of the way, if you’re talking about the entry point of the application or you don’t care about performance, then you should use _.Result
for a Task<_>
, and you should use _.Wait()
for a Task
(or just in general don’t care about any value that is returned). If you’re working with Async
instead of Task
, then use Async.RunSynchronously
. If your entire codebase is F#, then it’s probably more common to turn any Task
you run into immediately into an Async
via Async.AwaitTask
.
Assuming you really do want to block the thread and wait synchronously for the task to finish and get the result, then the first option you gave is the right one to use. It will internally do the t.Wait() if the Task is still running. If the Task completes with an exception, it will be thrown at the point of the t.Result property access.