How to use task builder right

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
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.

1 Like