user579342
user579342

Reputation: 93

async / await continuation

In the code below I need to execute the three Get... methods in parallel. When a Get... method completes I need to immediately call the Save... method. Note Save... takes thing as a parameter. All Get and Save methods must complete before DoStuffAsync returns.

My guess is that I need a continuation on the Get... methods but I dont know how to construct it.

protected async void DoStuffAsync()
{
    SomeThing thing = new SomeThing { ID = 5 };
    SomeRepository rep = new SomeRepository();

    // We need to run all three Get... methods in parallel
    // As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .

    var getRed = rep.GetRedAsync().ContinueWith<Task<string>>(async x => { thing.Color1 = x.Result; await rep.SaveRedAsync(thing); return x; }); // does not compile
    var getBlue = rep.GetBlueAsync();
    var getGreen = rep.GetGreenAsync();

    string red = await getRed.Result; // this is not good because getBlue may finish before getRed.  We want dont want to wait on getRed before calling SaveBlue
    await rep.SaveRedAsync(thing);
    var b = await getBlue;  
    var c = await getGreen;

    // thing must be fully initialized before DoStuffAsync returns

    }


public class SomeThing
{
    public int ID { get; set; }
    public string Color1 { get; set; }
    public string Color2 { get; set; }
    public string Color3 { get; set; }
}

public class SomeRepository
{
    public async Task<string> GetRedAsync()
    {
        return await Task.Run(() => "red");
    }

    public async Task<string> GetBlueAsync()
    {
        return await Task.Run(() => "blue");
    }

    public async Task<string> GetGreenAsync()
    {
        return await Task.Run(() => "green");
    }

    public async Task SaveRedAsync(SomeThing thing)
    {
        // We need thing.ID here as well as other properties
        await Task.Delay(1);
    }

    public async Task SaveBlueAsync(SomeThing thing)
    {
        await Task.Delay(1);
    }

    public async Task SaveGreenAsync(SomeThing thing)
    {
        await Task.Delay(1);
    }
}

Upvotes: 7

Views: 3048

Answers (3)

Dexters
Dexters

Reputation: 2495

You can try parallel framework:

using System.Threading;

using System.Threading.Tasks;

Func<Task<string>>[] functions = { rep.GetRedAsync, rep.GetBlueAsync, rep.GetGreenAsync }; 
Var[] GetArray = new Var[functions.Length]
int i=0;
Parallel.ForEach (var function in functions)  
{  
             GetArray[i++]=function();

}  

Note: Require .Net 4

Upvotes: -1

noseratio
noseratio

Reputation: 61686

I would not mix ContinueWith and await and rather use an async lambda directly:

protected async Task  DoStuffAsync()
{
    SomeThing thing = new SomeThing { ID = 5 };
    SomeRepository rep = new SomeRepository();

    // We need to run all three Get... methods in parallel
    // As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .

    Func<Task<X>> getRedFunc = async() =>
    {
        var result = await rep.GetRedAsync();
        thing.Color1 = result;
        await rep.SaveRedAsync(thing);
        return result;
    };

    var getRed = getRedFunc();

    var getBlue = rep.GetBlueAsync();
    var getGreen = rep.GetGreenAsync();

    await Task.WhenAll(getRed, getBlue, getGreen);
}

Also, don't use async void methods for anything but event handlers. You won't be able to observe the completion of a method like this or handle exceptions possibly thrown inside it.

Upvotes: 7

Jon Skeet
Jon Skeet

Reputation: 1500535

Well, you could explicitly use ContinueWith - or you could break off each "get and save" into a separate async method or async lambda. For example:

async Task GetAndSaveRedAsync(SomeThing thing, SomeRepository rep)
{
    var red = await rep.GetRedAsync();
    thing.Red = red;
    await SaveRedAsync(red);
    // Return red if you want (change the return type to Task<string>)
}

// Ditto for the others

Then:

protected async void DoStuffAsync()
{
    SomeThing thing = new SomeThing { ID = 5 };
    SomeRepository rep = new SomeRepository();

    var handleRed = GetAndSaveRedAsync(thing, rep);
    var handleBlue = GetAndSaveBlueAsync(thing, rep);
    var handleYellow = GetAndSaveYellowAsync(thing, rep);

    // Or use Task.WhenAll
    await handleRed;
    await handleBlue;
    await handleYellow;
}

Upvotes: 8

Related Questions