Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should I use Async.AwaitIAsyncResult instead of Async.AwaitTask to await a Task?

I want to await a Task<int> returned by Entity Framework Core when saving changes with the following call: myDbContext.SaveChangesAsync true

I found at least 3 different ways of achieving that but I am not really sure which one is the best considering that I don't really care about the int result part in Task<int>, at the end of the day I just wanna to await.

Using Async.AwaitIAsyncResult and then Async.AwaitIgnore to ignore the Task<bool>:

async {
    do! myDbContext.SaveChangesAsync true
    |> Async.AwaitIAsyncResult
    |> Async.AwaitIgnore
}

Upcasting the Task<int> to Task and then just awaiting that Task with Async.AwaitTask:

async {
    do! myDbContext.SaveChangesAsync true
    :> Task
    |> Async.AwaitTask
}

Awaiting Task<int> with Async.AwaitTask and ignoring the result with Async.Ignore

async {
    do! myDbContext.SaveChangesAsync true
    |> Async.AwaitTask
    |> Async.Ignore
}

AwaitIAsyncResult seems more relevant if I wanted to fetch a boolean in case of error while awaiting the task (and / or I wanted to have some control over the execution with a timeout): https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/async.awaitiasyncresult-method-%5Bfsharp%5D?f=255&MSPPError=-2147217396. I am also suspecting that this is the right if I wanted to avoid having an exception thrown to my face in case of something going wrong while awaiting and instead fetch a boolean.

Which leaves then the two other solutions, I would say that the last one translates better my intent of not caring about the int of Task<int> / Async<int>.

However I am not too sure, can someone can validate or invalidate my thoughts?

like image 439
Natalie Perret Avatar asked Nov 04 '25 22:11

Natalie Perret


2 Answers

The interface IAsyncResult is a legacy from the very early days of async on .NET (remember FileStream.BeginRead/FileStream.EndRead), so it's used only when interop with legacy APIs.

The fact that Async.AwaitIAsyncResult returns Async<bool> does not mean that it handles exceptions in any way, in fact it does not.

So with Async.AwaitIAsyncResult out of the way, I'll say that your third example

async {
    do! myDbContext.SaveChangesAsync true
    |> Async.AwaitTask
    |> Async.Ignore
}

is the way to go, since the intent is explicit.

like image 190
nilekirk Avatar answered Nov 07 '25 02:11

nilekirk


I can't see how your example would compile so I hope I'm not missing something here.

I think this is how I'd tackle it:

open FSharp.Control.Tasks.V2 // From nuget package Taskbuilder.fs

let someExampleAsync (thing: bool) : Threading.Tasks.Task<int> = 
    task {
        printfn "this got called"
        return 1
    }

let saveChanges() : Async<int> = someExampleAsync true |> Async.AwaitTask

// await once
saveChanges() |> Async.RunSynchronously |> ignore

// await a few times
let repeatIt = 
    async {
        let! awaitOnce = saveChanges()
        let! awaitAgain = saveChanges()
        let! awaitAndAgain = saveChanges()
        return "all done"
    }

repeatIt |> Async.RunSynchronously |> ignore

Disclaimer: I'm a beginner.

like image 26
drkmtr Avatar answered Nov 07 '25 02:11

drkmtr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!