Search code examples
c#asynchronouslambdaasync-awaittask

Right way to create and call lambda of async methods in C#


I got some hard time figuring out how to "store" and call async lambdas in C#, it was basically trying by running. I find out some approach that work while others do not (below), I read about it but it it still unclear why some work and others not, can someone explain why and maybe add a better approach as well?

public static class Program
{
    static async Task Main(string[] args)
    {
        // works fine, everything is printed
        Console.WriteLine("Before 1 delay");
        await Task.Delay(1000);
        Console.WriteLine("After 1 delay");

        // works fine, everything is printed
        var foo = new Func<Task>(async () =>
        {
            Console.WriteLine("Before 2 delay");
            await Task.Delay(1000);
            Console.WriteLine("After 2 delay");
        });
        await foo.Invoke();

        // works fine, everything is printed
        var foo2 = new Action(() =>
        {
            Console.WriteLine("Before 3 delay");
            Task.Delay(1000).Wait();
            Console.WriteLine("After 3 delay");
        });
        foo2.Invoke();

        // Does not work, just before is printed
        var foo3 = new Action(async () =>
        {
            Console.WriteLine("Before 4 delay");
            await Task.Delay(1000);
            Console.WriteLine("After 4 delay");
        });
        foo3.Invoke();
    }
}

Please notice that question is not the same as the following below, even when the answer is the same the context is different, as this question problem is basically why an Action delegate does run async even with the await being there, besides of that there is the "bonus" of "Why .Await() works, while await do not in a action delegate"

async Task vs async void “await” doesn't wait for the completion of call Why is the console window closing immediately once displayed my output? How to stop C# console applications from closing automatically?


Solution

  • The problem in your last approach is that you're wrapping an async lambda with an Action, which describes a void-returning function.

    When await Task.Delay(1000); executes, it tells the runtime to schedule the continuation (i.e. the instructions after that line) after the delay completes asynchronously.

    The caller of the delegate at that point has no way of awaiting the inner-async function and its delay, so here we're in the context of an async void invocation, where the operation will eventually complete but no Task-like instance is returned. Since the caller is unable to wait for the function to terminate, it continues its execution until the Main method exits.

    In general, async void should only be used for asynchronous event handlers, because as you saw doesn't allow the caller to properly await the function to complete.

    Right way to create and call lambda of async methods in C#

    // return a Task!
    var wrapper = new Func<Task>(async () =>
    {
        await SomethingAsync();
    });
    await wrapper.Invoke();