42

I've this method

public void Execute(Action action)
{
    try
    {
        action();
    }
    finally
    {
    }
}

and I need to convert it to an async method call like this one

public async Task ExecuteAsync(Action action)
{
    try
    {
        await action();
    }
    finally
    {
    }
}

The problem with the code above is that the compiler issue the following error

The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.

vcRobe
  • 1,671
  • 3
  • 17
  • 35

3 Answers3

59

If you want to await on a delegate, it has to be of type Func<Task> or Func<Task<T>>. An Action is equivalent into a void Action() named method. You can't await on void, yet you can await Task Func() or Task<T> Func:

public async Task ExecuteAsync(Func<Task> func)
{
    try
    {
        await func();
    }
    finally
    {
    }
}

If this can't be done, it means that internally the method isn't truly asynchronous, and what you actually want to do is execute the synchronous delegate on a thread-pool thread, which is a different matter, and isn't really executing something asynchronously. In that case, wrapping the call with Task.Run will suffice.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 6
    Yes also what confuses people is the fact that c# compiler allows the type of `async () => { await ... }` to be either `Action` or `Func`. And if people choose to type it with `Action` then they have difficulty `await` on it. If they wisely choose to use `Func`, it is now awaitable. That's one of the good thing about using `Func` over `Action`, among others. I really don't like that the compiler allows `Action` to be the type in this case for the clarity. – KFL Apr 10 '17 at 20:31
  • 1
    @KFL Many people feel the same. The reason this does work on `Action` is because the designers of this feature wanted to make it compatible with event handlers which are naturally `void` returning methods. – Yuval Itzchakov Apr 11 '17 at 07:44
6

Try this:

public async void ExecuteAsync(Action action)
{
    await Task.Run(action); 
}

Using Task.Run()creates an awaitable by running your action on a different task. And also bear in mind that handling exceptions on "awaitables" does not work as intended.

Better wrap that action() call in a try catch an do Task.Run() on that wrapper.

Tamas Ionut
  • 4,240
  • 5
  • 36
  • 59
1

Let's simplify your starting point down to:

public void Execute(Action action)
{
  action();
}

Because you say the finally isn't important.

Valid but pointless:

public async Task ExecuteAsync(Action action)
{
    action();
}

This will be pretty much the same as doing:

public Task ExecuteAsync(Action action)
{
  action();
  return Task.FromResult(0);
}

That is, it'll do what the non-async was doing, and then return a "completed" Task so nothing is gained. It's worth noting though as it's often a valid part-way-point in moving from non-async to async.

Better:

public async Task ExecuteAsync(Action action)
{
  await Task.Run(() => action()); 
}

Which in this case, because it's a single void-returning call can be simplified to:

public async Task ExecuteAsync(Action action)
{
  await Task.Run(action); 
}

Whether this is worth doing or not is another matter. This releases the current thread from being used, but transfers to another thread to do the work. If we're just going to await the result of this when it's called then we might as well just call the non-async version and be done with it. If however we're doing WaitAll in the caller, or something else that hence benefits from this, then it could indeed be useful.

Potentially much better though is:

public async Task ExecuteAsync(Action action)
{
  await actionAsync();
}

Here there's an Async version of the method we are calling, so we change to make use of that. Now, that could be just the same as the above if actionAsync just spins up a thread or uses the thread pool. If however actionAsync does something using asynchronous I/O then there's a much bigger benefit to this.

Note that in this case we could have just tail-called the Task we get:

public Task ExecuteAsync(Action action)
{
  return actionAsync();
}

However, that wouldn't be the same if we needed something done after an await within our method. E.g.:

public void Execute(Action action)
{
  action();
  otherAction();
}

Would have to become:

public async Task Exectute(Action action)
{
  await actionAsync();
  await otherActionAsync();
}

Or if otherAction had no async version:

public async Task Exectute(Action action)
{
  await actionAsync();
  otherAction();
}
Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • When having two async methods we could use Task.WhenAll() – Tamas Ionut Nov 26 '15 at 15:33
  • 3
    @TamasIonut not if you require one to be completed before the other is begun. – Jon Hanna Nov 26 '15 at 16:14
  • @TamasIonut I was particularly thinking about the case where a Task cannot be tail-called because something is done after it completes, hence that's the case relevant to what I was writing about. Certainly the if we didn't have such a dependency then the second-last could become `return Task.WaitAll(actionAsync(), otherActionAsync());`. – Jon Hanna Nov 26 '15 at 16:37