Search code examples
lambdaasync-awaitblazor

Binding a one-line lambda expression. Does it need to be async when calling async Task method?


Let's say I have an async Method:

private async Task DoSomethingAsync(int value)
{
    await Library.SomethingAwesomeAsync(value);
}

Now I have an input-control:

<input type="text" @bind="FancyProperty" @bind:after="() => DoSomethingAsync(inputValue)" />

This does not give me a warning. But I'd expect I need to write the following:

<input type="text" @bind="FancyProperty" @bind:after="async () => await DoSomethingAsync(inputValue)" />

Does anyone know why both ways seem valid?

When writing the following inside a normal C# method, I get a warning ("Because this call is not awaitet, execution of the method continues before...."):

Task task = new Task(() => DoSomethingAsync(inputValue));

Solution

  • @bind:after expects you to return a Task?.

    That's what you do in the first example, so it compiles. It's the normal pattern to use when passing an value into the called handler.

    The second example works but is overkill, wrapping an async method within an anonymous async method.

    You can also return a void. If the method is synchronous it runs to completion before the final component render. But if it yields, as in async void, then the UI handler runs to completion, and renders the component, before the method completes [there's no returned Task for the UI handler to await].

    Only use the void pattern in synchronous methods.

    You can always do this.

    
    private Task Handler()
    {
      //.... Add async code later
      return Task.CompletedTask;
    }
    

    It's cheap because Task.CompletedTask is static and returns a cached Task. Source below.

    /// <summary>Gets a task that's already been completed successfully.</summary>
    public static Task CompletedTask => s_cachedCompleted;