Extending C# async/await await doesn't await

async/await c#
c# async extension methods
c# async/await under the hood
how async/await works c#
cannot await void
c# wait for async method to complete
async task return value c#
when was async/await introduced c#

Extending: C# async/await await doesn't await

In case the methods executed sequentially are stored in a list as well, how can I add the ExecuteParallelAsync to that list?

private async Task ExecuteSequential()
{
    List<Action> sequentialMethods = new List<Action>()
    {
        SomeMethod1,
        SomeMethod2,
        await ExecuteParallelAsync, // ???
        SomeMethod3,
        SomeMethod4
    };

    for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
    {
        sequentialMethods.ElementAt( i ).Invoke();
    }
}

Clarification:

private async Task ExecuteParallelAsync()
{
    List<Action> methods = new List<Action>()
    {
        MyMethod1,
        MyMethod2,
        MyMethod3
    };


    await Task.Run( () => { Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() ); } );            
}

sequentialMethods is a List<Action> but ExecuteParallelAsync is NOT an Action. I've tried to split the list, like suggested. Unfortunately, it doesn't help.

The way suggested by Marc from the original code works fine. But I want to do some extra stuff after each (sequential) method call, which is why I'm trying to use a list and a loop instead of plain method calls.

But when I do so, I'm facing the original problem again, that SomeMethod3 is executed before ExecuteParallelAsync is finished.

Once again, everything in ExecuteParallelAsync can and should be executed simultaneously. Everything in ExecuteSequential has to be executed sequentially.

Solution:

Gabriel is absolutely correct. The crucial statement is this

await Task.Run( () => { Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() ); } );

When I remove every async and every Task, so that everything is intended to run be executed sequentially this line is the key:

Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() );

When I use this last statement, everything works correctly.

Thanks to everyone, all your ideas, thoughts and efforts helped and are appreciated.

I'm facing the original problem again, that SomeMethod3 is executed before ExecuteParallelAsync is finished

Then this is not a use case for asynchronous programming. Your requirement is that this is synchronous.

That is especially so since you said that MyMethod1/MyMethod2/MyMethod3 are not asynchronous methods. If they were, that would be a totally different thing. But since they aren't, I don't see any value in trying to use async and await here.

But don't confuse asynchronous with parallel. It seems you want the methods called in ExecuteParallelAsync to be run in parallel, that's fine. You just don't need async and await.

For example:

private void ExecuteSequential()
{
    List<Action> sequentialMethods = new List<Action>()
    {
        SomeMethod1,
        SomeMethod2,
        ExecuteParallel,
        SomeMethod3,
        SomeMethod4
    };

    for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
    {
        sequentialMethods.ElementAt( i ).Invoke();
    }
}

private void ExecuteParallel()
{
    List<Action> methods = new List<Action>()
    {
        MyMethod1,
        MyMethod2,
        MyMethod3
    };

    Parallel.ForEach( methods , ( currentMethod ) => currentMethod.Invoke() );            
}

Can't await async extension method, The error message is pretty clear: the method where you're calling the extension method should be marked as async . public Task<string> MyExtension(this  End Function Async Function CalledMethodAsync (howLong As Integer) As Task ResultsTextBox.Text &= vbCrLf & " Entering called method, starting and awaiting Task.Delay." ' Slow the process down a little so you can distinguish between awaiting ' and not awaiting. Adjust the value for howLong if necessary.

You can create an Action that when invoked will start the task and wait for it to complete, like this:

List<Action> sequentialMethods = new List<Action>()
{
    SomeMethod1,
    SomeMethod2,
    () => ExecuteParallelAsync().Wait(),
    SomeMethod3,
    SomeMethod4
};

Extending the async methods in C#, But the C# compiler does not expect that these well-known types come from You can implement UnsafeAwaitOnComplete method to test the  When the code awaits an incomplete awaiter, it similarly sets the <>1__state (to the location of the code that checks the result of the then-completed awaited operation) and uses the <>t__builder to hook up a continuation that will cause the implementation to call MoveNext again (at which point it will jump to the location dictated by <>1__state).

Here's a version:

private async Task  ExecuteSequential()
{
    var sequentialMethods = new List<Func<Task>>()
    {
        () => Task.Run(SomeMethod1),
        () => Task.Run(() => SomeMethod2("Hey!")), 
        ExecuteParallelAsync, 
        () => Task.Run(SomeMethod3),
        () => Task.Run(SomeMethod4)
    };

    for ( int i = 0 ; i < sequentialMethods.Count ; i++ )
    {
        Task t = sequentialMethods[i].Invoke(); 
        await t;
        // or just await sequentialMethods[i]();
    }
}

Just for the record, ExecuteSequential could just be a standard async method (which are very good in executing things sequentially).

private async Task ExecuteSequential()
{
  SomeMethod1();
  SomeMethod2();
  await ExecuteParallelAsync(); 
  SomeMethod3();
  SomeMethod4();
};

Edit: Test
Code
class Program
{
    public void MyMethod1() => Console.WriteLine("||My1");
    public void MyMethod2() => Console.WriteLine("||My2");
    public void MyMethod3() => Console.WriteLine("||My3");

    private async Task ExecuteParallelAsync()
    {
        Console.WriteLine("||Start");
        List<Action> methods = new List<Action>() { MyMethod1, MyMethod2, MyMethod3 };

        await Task.Run(() => { Parallel.ForEach(methods, 
           (currentMethod) => currentMethod.Invoke()); }); // This could be just 'currentMethod();' (no Invoke())
        Console.WriteLine("||End");
    }


    public void SomeMethod1() => Console.WriteLine("Some1");
    public void SomeMethod2(string s) => Console.WriteLine($"Some2: {s}");
    public void SomeMethod3() => Console.WriteLine("Some3");
    public void SomeMethod4() => Console.WriteLine("Some4");

    private async Task ExecuteSequential()
    {
        var sequentialMethods = new List<Func<Task>>()
        {
           () => Task.Run(SomeMethod1),
           () => Task.Run(() => SomeMethod2("Hey!")),
           ExecuteParallelAsync,
           () => Task.Run(SomeMethod3),
           () => Task.Run(SomeMethod4)
        };

        for (int i = 0; i < sequentialMethods.Count; i++)
        {
            await sequentialMethods[i]();
        }
    }

    static async Task Main(string[] args)
    {
        await new Program().ExecuteSequential();
        Console.WriteLine("All done");
    }
}
Output
Some1
Some2: Hey!
||Start
||My3
||My2
||My1
||End
Some3
Some4
All done

My1, My2, and My3 will change order between executions, but always within ||Start and ||End.

Async/Await, These days there's a wealth of information about the new async and await and the notion of an event handler actually returning something doesn't make much sense If you follow this solution, you'll see async code expand to its entry point,​  .NET added async/await to the languages and libraries over seven years ago. In that time, it’s caught on like wildfire, not only across the .NET ecosystem, but also being replicated in a myriad of other languages and frameworks. It’s also seen a ton of improvements in .NET,

Dissecting the async methods in C#, Extending the async methods in C#. Async/await helps to compose tasks and gives the user an ability to use well-known constructs like try/catch , using etc. But like It doesn't mean that the method is asynchronous at all. Async and await Now we can look at how to implement this property using the new async and await. button{data: { action:"classA#methodA classB#methodB" } } in class A. Inherently asynchronous events-Example: addEventListener('click'). 5, the C# and the new characteristics of the increase of — — async and await C#5.

Understanding Control Flow with Async and Await in C#, Understanding Control Flow with Async and Await in C# The await keyword does not block the current thread. in the form of a text-based dashboard; you could extend that idea to periodically check for keyboard input. Chrome Extension Async. Promise wrapper for the Chrome extension API so that it can be used with async/await rather than callbacks. The Extension API provided by Chrome uses callbacks. However, Chrome now supports async and await keywords. This library wraps Chrome extension API callback methods in promises, so that they can be called with

C# Async Antipatterns, The async and await keywords have done a great job of simplifying writing a synchronous method (i.e. one that doesn't return a Task or Task<T> ) but you Some people prefer to make this into an extension method, called  In the previous guide in this series we took a look at the basics of the async and await keywords in C#. Once you get the hang of their syntax and usage, it can actually be quite a joy to write asynchronous code. In fact, it feels so natural that one can forget that the code is asynchronous!

Comments
  • Await is syntactic sugar to cut the method into a statemachine. How would it cut a list in muliple methods? The await should be done when calling a method and cannot be generated dynamically. This is done at compile time.
  • In my experience, ParallelFor doesn't play well with async functions.
  • This also depends on how MyMethod1/MyMethod2/MyMethod3 are implemented. Are they async methods?
  • No, they aren't. They are plain protected virtual void in the base class and therefore protected override void in the derived class. The only two methods that I have changed are ExecuteSequential and ExecuteParallelAsync from (async) void to async Task.
  • If they're not async, you can't await them. Your creating a task, to await it, then creating other tasks to run non-async code. Why? This is just making your code less efficient for no benefit. Just use a parallel for <full stop> (which is basically what Gabirel has said)
  • Thanks you very much Gabriel. I found my mistake. I'm going to edit the original post and post the solution.
  • Thanks Theodor, but nothing changes. Is my approach wrong entirely?
  • The methods are invoked in this order: SomeMethod1, then SomeMethod2, then (in parallel: SomeMethod1 and SomeMethod2 and SomeMethod3), then SomeMethod3, and then finally SomeMethod4. Isn't this what you want? If you don't want the SomeMethod1, SomeMethod2 and SomeMethod3 to be invoked twice, just remove them from the list.
  • It's exactly what I want. However, in the real application the methods are invoked in this order: SomeMethod1, SomeMethod2, first method in ExecuteParallelAsync, SomeMethod3, SomeMethod 4, SecondMethod from ExecuteParallelAsync, some methods in the application, third method from ExecuteParralelAsync, and so on. So the key is, ExecuteParallelAsync has to be finished before SomeMethod3 is executed, because if not the application crashes later on. Is everything executed sequentially, the application is getting slow.
  • I strongly suspect that MyMethod1, MyMethod2 and MyMethod3 are async void methods.
  • No, they aren't. They are plain protected virtual void in the base class and therefore protected override void in the derived class. The only two methods that I have changed are ExecuteSequential and ExecuteParallelAsync from void to async Task.
  • Thank you very much for this new approach. Unfortunately, it doesn't change anything. (Refers to your answer before the edit)
  • What do you mean by 'Unfortunately, it doesn't change anything.'? Both approaches will result in sequential execution.
  • I added a test.
  • Thank you very much @tymtam for all your efforts, I really do appreciate it. I've copied your test setup and it works absolutely correct. In my real application however, it doesn't work. So I guess, the simultaneous execution isn't the issue. But what could it be?