Click here to Skip to main content
15,878,959 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
Hi,

My issue is the following :
I have n Tasks that run in parallel.
I want each Task to have its own continuation that uses the result. (Think of a UI update, for each finished task)

If I were only using Framework 4.0 Task, I would use the ContinueWith on each task.
But I want to use await / async , and I don't find a way to do something after each task is finished, without one waiting the other.

An example of code to transform :

 public async void MainMethod()
        {
            var doSomething = DoSomething();
            var doSomethingElse = DoSomethingElse();
            var doSomethingDifferent = DoSomethingDifferent();

            //These three continuations do not interfer with each other.
            doSomething.ContinueWith(task => ReactToDoSomething(task.Result));
            doSomethingElse.ContinueWith(task => ReactToDoSomethingElse(task.Result));
    doSomethingDifferent.ContinueWith(task=>ReactToDoSomethingDifferent(task.Result));
        }

        public Task<int> DoSomething()
        {
            return Task.FromResult(1);
        }

        public Task<int> DoSomethingElse()
        {
            return Task.FromResult(2);
        }

        public Task<int> DoSomethingDifferent()
        {
            return Task.FromResult(3);
        }
///The code of the react methods must not change.                            
            public void ReactToDoSomething(int i)
        {
            //Update a part of UI 
        }

        public void ReactToDoSomethingElse(int i)
        {
            //Update a part of UI 
        }

        public void ReactToDoSomethingDifferent(int i)
        {
            //Update a part of UI 
        }
    }



Thanks for your help.

What I have tried:

First my idea was to replace the code of the reacts methods, but then it came that it is a limitation : do not change this part of code. (Used somewhere else)

Still, here is what I did :

  public class MyClass
    {
        public async void MainMethod()
        {
            var doSomething = DoSomething();
            var doSomethingElse = DoSomethingElse();
            var doSomethingDifferent = DoSomethingDifferent();

            //These three continuations do not interfer with each other.
            ReactToDoSomething(doSomething);
            ReactToDoSomethingElse(doSomethingElse);
            ReactToDoSomethingDifferent(doSomethingDifferent);
        }

        public Task<int> DoSomething()
        {
            return Task.FromResult(1);
        }

        public Task<int> DoSomethingElse()
        {
            return Task.FromResult(2);
        }

        public Task<int> DoSomethingDifferent()
        {
            return Task.FromResult(3);
        }
//Changed React methods but must not, and this is my issue.
        public async Task ReactToDoSomething(Task<int> task)
        {
            //Update a part of UI 
        }

        public async void ReactToDoSomethingElse(Task<int> task)
        {
            //Update a part of UI 
        }

        public async void ReactToDoSomethingDifferent(Task<int> task)
        {
            //Update a part of UI 
        }
    }
Posted
Updated 27-Jul-18 8:20am

You just need to await the task in the continuation methods.

You'll probably also want to wait for all of the continuations to finish in your MainMethod.
C#
public async Task ReactToDoSomething(Task<int> task)
{
    int result = await task;
    // Do something here...
}

// Same for other "react to" methods...

public async Task MainMethod()
{
    Task<int> doSomething = DoSomething();
    Task<int> doSomethingElse = DoSomethingElse();
    Task<int> doSomethingDifferent = DoSomethingDifferent();

    Task continuation1 = ReactToDoSomething(doSomething);
    Task continuation2 = ReactToDoSomethingElse(doSomethingElse);
    Task continuation3 = ReactToDoSomethingDifferent(doSomethingDifferent);
    
    await Task.WhenAll(continuation1, continuation2, continuation3);
}



EDIT: As discussed in the comments, if you don't want to change the original "ReactTo" methods, you'll need a wrapper method for the continuation:
C#
private async Task ReactTo<TResult>(Task<TResult> task, Action<TResult> react)
{
    TResult value = await task;
    react(value);
}

public async Task MainMethod()
{
    Task<int> doSomething = DoSomething();
    Task<int> doSomethingElse = DoSomethingElse();
    Task<int> doSomethingDifferent = DoSomethingDifferent();
    
    Task continuation1 = ReactTo(doSomething, ReactToDoSomething);
    Task continuation2 = ReactTo(doSomethingElse, ReactToDoSomethingElse);
    Task continuation3 = ReactTo(doSomethingDifferent, ReactToDoSomethingDifferent);
    
    await Task.WhenAll(continuation1, continuation2, continuation3);
}
 
Share this answer
 
v2
Comments
Vasilievski 30-Jul-18 4:58am    
Hello, thanks for the answer.
As I mentioned, the goal is to not change the initial continuations methods.
Since they are not async, you can't await from it, right ?
Richard Deeming 30-Jul-18 16:49pm    
No, you can't await from something that's not async.

If you want to leave the original ReactTo... methods as they are currently, you're going to need another async method for the continuation. For example:
private async Task ReactTo<TResult>(Task<TResult> task, Action<TResult> react)
{
    TResult value = await task;
    react(value);
}

public async Task MainMethod()
{
    Task<int> doSomething = DoSomething();
    Task<int> doSomethingElse = DoSomethingElse();
    Task<int> doSomethingDifferent = DoSomethingDifferent();
    
    Task continuation1 = ReactTo(doSomething, ReactToDoSomething);
    Task continuation2 = ReactTo(doSomethingElse, ReactToDoSomethingElse);
    Task continuation3 = ReactTo(doSomethingDifferent, ReactToDoSomethingDifferent);
    
    await Task.WhenAll(continuation1, continuation2, continuation3);
}
Vasilievski 1-Aug-18 8:38am    
This wrapper is what I actually needed. Please, change your answer so that I mark it as accepted. Thanks a lot.

It may be a good idea to restructure your class to facilitate using the Task-based Asynchronous Pattern. I suggest that you change the signatures of some of your methods. Async void methods should be reserved for event handlers. My suggestion would be something like this.


C#
 public async Task MainMethod()
 {
     List<Task> tasks = new List<Task>() {
         DoSomething(),
         DoSomethingElse(),
         DoSomethingDifferent() };

     await Task.WhenAll(tasks);
 }

public async Task DoSomething()
 {
     int result= await Task.FromResult(1);
     if(result>0)
     ReactToDoSomething(result);
 }

 public void ReactToDoSomething(int i)
 {
     //Update a part of UI
 }
 
Share this answer
 
v2
Comments
Vasilievski 30-Jul-18 4:59am    
Hello, thanks for the answer.
What you're doing is tightly coupling a reaction to something happening.
What if I want to reuse DoSomething without changing the UI (calling the 'ReactTo...' part) ?
George Swan 30-Jul-18 9:49am    
I am not sure what you mean by 'without changing the UI'. Could you elucidate please?
Vasilievski 30-Jul-18 10:36am    
Sure. Think about DoSomething as an individual action, like computing a result. Sometimes I want the UI to reflect this change, then I call ReactToDoSomething. Nonetheless, there are times I don't want to reflect the change, then I don't call ReactToDoSomething. Your idea is to merge both actions, so I don't have the choice to reflect or not the change.
George Swan 30-Jul-18 15:37pm    
I have modified the DoSomething method so that ReactToDoSomething is only called if result>0
Vasilievski 1-Aug-18 8:39am    
This solution seems weird.. Don't you think so ?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900