Skip to content

Instantly share code, notes, and snippets.

@davidfowl
Created February 4, 2021 16:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save davidfowl/c72f0973919e0e18708a5c345a2a4778 to your computer and use it in GitHub Desktop.
Save davidfowl/c72f0973919e0e18708a5c345a2a4778 to your computer and use it in GitHub Desktop.
var t1 = DoFooAsync(obj);
var t2 = DoBarAsync(obj);
var t = await WhenAnySuccessOrAllFail(t1, t2);
async Task WhenAnySuccessOrAllFail(params Task[] tasks)
{
var remaining = new List<Task>(tasks);
while (remaining.Count > 0)
{
var t = await Task.WhenAny(remaining);
if (t.IsCompletedSuccessfully)
{
// Successful so return it
return t;
}
// It failed, remove it from the remaining tasks to process
remaining.Remove(t);
}
// Both failed (will bubble an aggregate exception)
return await Task.WhenAll(tasks);
}
@dustinmoris
Copy link

dustinmoris commented Feb 4, 2021

Thanks!

I don't do exceptions*, so in my case I guess I could do something like this based on your solution!

async Task<(bool, T)> WhenAnySuccessOrAllFail(Func<T, bool> checkSuccess, params Task<T>[] tasks)
{
    var remaining = new List<Task>(tasks);

    while (remaining.Count > 0)
    {
        var t = await Task.WhenAny(remaining);
        if (checkSuccess(t))
        {
            return (true, t);
        }

        // It failed, remove it from the remaining tasks to process
        remaining.Remove(t);
    }

    // Both failed
    return (false, null);
}

*) I catch exceptions where they occur immediately and then map them to a result type so that my own code doesn't have to deal with exceptions. If there is a genuine exception type which means that my program cannot operate anymore then I'd let it bubble up to be logged and crashed.

@dustinmoris
Copy link

F# code I ended up with:

    let rec waitForFirstSuccess (tasks : Task<Result<string, string>> list) =
        task {
            let! task = Task.WhenAny(tasks)
            match task.Result with
            | Ok _    -> return task.Result
            | Error _ ->
                return!
                    tasks
                    |> List.filter(fun t -> t = task)
                    |> waitForFirstSuccess
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment