Skip to content

Async.Await overload (esp. AwaitTask without throwing AggregateException) #840

@codingedgar

Description

@codingedgar

I propose including AwaitTaskCorrect (maybe with another name)

open System
open System.Threading.Tasks

type Async with
    static member AwaitTaskCorrect(task : Task) : Async<unit> =
        Async.FromContinuations(fun (sc,ec,cc) ->
            task.ContinueWith(fun (task:Task) ->
                if task.IsFaulted then
                    let e = task.Exception
                    if e.InnerExceptions.Count = 1 then ec e.InnerExceptions.[0]
                    else ec e
                elif task.IsCanceled then
                    ec(TaskCanceledException())
                else
                    sc ())
            |> ignore)

    static member AwaitTaskCorrect(task : Task<'T>) : Async<'T> =
        Async.FromContinuations(fun (sc,ec,cc) ->
            task.ContinueWith(fun (task:Task<'T>) ->
                if task.IsFaulted then
                    let e = task.Exception
                    if e.InnerExceptions.Count = 1 then ec e.InnerExceptions.[0]
                    else ec e
                elif task.IsCanceled then
                    ec(TaskCanceledException())
                else
                    sc task.Result)
            |> ignore)


// examples

let mkFailingTask exn = Task.Factory.StartNew<_>(fun () -> raise exn)

let test1 taskAwaiter =
    async {
        try
            return! taskAwaiter (mkFailingTask (ArgumentException()))
        with
        | :? ArgumentException -> return true
        | :? AggregateException -> return false
    } |> Async.RunSynchronously


test1 Async.AwaitTask // false
test1 Async.AwaitTaskCorrect // true


let test2 taskAwaiter =
    async {
        try
            return! taskAwaiter (mkFailingTask (AggregateException("kaboom!")))
        with
        | :? AggregateException as e when e.Message = "kaboom!" -> return true
        | :? AggregateException -> return false
    } |> Async.RunSynchronously

test2 Async.AwaitTask // false
test2 Async.AwaitTaskCorrect // true

The existing way of approaching this problem in F# is copying the snippet in your project, ref : jet/equinox#198

Pros and Cons

The advantages of making this adjustment to F#:

  • Provides a better implementation of Async.AwaitTask where the actual exception of a failing task is passed to the async mechanism.
  • Works as C# would handle taks

The disadvantages of making this adjustment to F# are:

  • Might be confusing having 2 AwaitTask
  • If AwaitTask is "fixed" then is a breaking change

Extra information

Estimated cost (XS, S, M, L, XL, XXL): No idea

Related suggestions: at this moment I don't know of any other related suggestions might edit in the future

Affidavit (please submit!)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I or my company would be willing to help implement and/or test this

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions