Should I Worry About "This Async Method Lacks 'Await' Operators and Will Run Synchronously" Warning

Should I worry about This async method lacks 'await' operators and will run synchronously warning

The async keyword is merely an implementation detail of a method; it isn’t part of the method signature. If a particular method implementation or override has nothing to await, then just omit the async keyword and return a completed task using Task.FromResult<TResult>:

public Task<string> Foo()               //    public async Task<string> Foo()
{ // {
Baz(); // Baz();
return Task.FromResult("Hello"); // return "Hello";
} // }

If your method return type is Task instead of Task<TResult>, then return Task.CompletedTask:

public Task Bar()                       //    public async Task Bar()
{ // {
Baz(); // Baz();
return Task.CompletedTask; //
} // }

Note: Task.CompletedTask was added in .NET Framework 4.6. If you’re targeting .NET Framework 4.5.2 or earlier, then you can instead return a completed task of any type and value. Task.FromResult(0) seems to be a popular choice:

public Task Bar()                       //    public async Task Bar()
{ // {
Baz(); // Baz();
return Task.FromResult(0); //
} // }

Dealing with Exceptions

An exception thrown by a non-async method propagates immediately up the call stack, but an exception thrown by an async method is stored in the returned Task object and propagates only when the Task is awaited. This makes a big difference if someone calls your method and then does something else before awaiting the Task:

Task<string> task = Foo();   // If Foo is async and throws an exception,
DoSomethingElse(); // then this line will be executed,
string result = await task; // and the exception will be rethrown here.

If you need to preserve this behavior for a non-async method, then wrap the entire method within a try...catch statement. Pass any unhandled exception to Task.FromException, and return the result:

public Task<string> Foo()                       //  public async Task<string> Foo()
{ // {
try //
{ //
Baz(); // might throw // Baz();
return Task.FromResult("Hello"); // return "Hello";
} //
catch (Exception ex) //
{ //
return Task.FromException<string>(ex); //
} //
} // }

public Task Bar() // public async Task Bar()
{ // {
try //
{ //
Baz(); // might throw // Baz();
return Task.CompletedTask; //
} //
catch (Exception ex) //
{ //
return Task.FromException(ex); //
} //
} // }

The generic argument to Task.FromException must match the return type of the method.

Reducing Boilerplate Code

You can use the following helper class to automatically call Task.FromResult and Task.FromException for you:

public static class TaskHelper
{
public static Task FromResultOf(Action action)
{
try
{
action();
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException(ex);
}
}

public static Task<T> FromResultOf<T>(Func<T> func)
{
try
{
return Task.FromResult(func());
}
catch (Exception ex)
{
return Task.FromException<T>(ex);
}
}
}

Sample usage:

public Task<string> Foo()               //    public async Task<string> Foo()
{ // {
return TaskHelper.FromResultOf( //
() => //
{ //
Baz(); // Baz();
return "Hello"; // return "Hello";
}); //
} // }

public Task Bar() // public async Task Bar()
{ // {
return TaskHelper.FromResultOf( //
() => //
{ //
Baz(); // Baz();
}); //
} // }

This async method lacks 'await' operators and will run synchronously

The async keyword, by itself, doesn't really do much. Remove it from your code and your code will act exactly the same.

What does async do?

  • It changes what's valid inside of the method, specifically it allows you to use the await keyword
  • In turn, it means that the body of the method will be transformed, based on the awaits that are present in the body of the method.
  • And if the method returns a value, the method is also transformed to wrap the return value in a Task.

However, if you a) Don't have any awaits in your method body and b) are void returning, then nothing special will be achieved. The compiler warning does try to be clear about this - an async method without any awaits just plain doesn't make sense. awaits are the more important part of this feature.

c# async await strange warning CS1998: This async method lacks 'await' operators

Your await is within a lambda expression, which is another function entirely to your StartSearchAsync method.

In fact you should not be passing an async delegate to List<T>.ForEach, as that converts the delegate to async void, which is undesirable because the calling method cannot wait for the delegates to complete.

A better option would be to use Enumerable.Select, in combination with Task.WhenAll:

public async Task<List<T1>> StartSearchAsync()
{
....other code

var tasks = searchRequests.Select(SinglePageSearch);
var results = await Task.WhenAll(tasks);

foreach (result in results) products.AddRange(result);

return products;
}

Using this approach, Task.WhenAll enumerates the Tasks generated by Select, and creates another Task that completes when each SinglePageSearch has completed.

Now StartSearchAsync can await their completion.

And if products is simply an empty list being used to amalgamate the results, you can simplify further:

public async Task<List<T1>> StartSearchAsync()
{
....other code

var results = await Task.WhenAll(searchRequests.Select(SinglePageSearch));

return results.SelectMany(x => x).ToList();
}

Async method lacks await operators warning when the await is in a function passed to an async lambda expresion

First, I recommend using Polly. It's widely used, extensively tested, and has native support for asynchronous as well as synchronous usage.

But if you want to keep using your own, you can add a synchronous equivalent:

public async Task RetryAsync(Func<Task> _action, int _ms = 1000, int _counter = 3);
public void Retry(Action _action, int _ms = 1000, int _counter = 3);

which can be called as such:

Retry(() => {
_newID = myDBFunction();
}, 300);

If you want to always put synchronous code on a thread pool, you can add an overload for that, to:

public async Task RetryAsync(Func<Task> _action, int _ms = 1000, int _counter = 3);
public async Task RetryAsync(Action _action, int _ms = 1000, int _counter = 3) =>
await RetryAsync(() => Task.Run(_action), _ms, _counter);

which can be called as such:

await RetryAsync(() => {
_newID = myDBFunction();
}, 300);

Usage of Task.WaitAll without any 'await' operators causing warning CS1998 This async method lacks 'await' operators and will run synchronously

will it really make any part of the code to run synchronously, just because the lack of an 'await' operator?

Yes. The Main method will run synchronously. This won't really matter because it's the Main method, but if you want to asynchronously wait for the tasks to complete, use await Task.WhenAll instead of Task.WaitAll. The asynchronous approach has an additional benefit in that it doesn't wrap exceptions in AggregateException.

On a side note, use await instead of ContinueWith.

In ViewComponent: This async method lacks 'await' operators and will run synchronously

You don't use any asynchronous call in the method (there is no await), hence the warning. ViewComponent has 2 methods InvokeAsync and Invoke. You should use the synchonous version (Invoke) of the ViewComponent when there is no asynchrounous calls in the implementation:

public class GenericReportViewComponent : ViewComponent
{
public IViewComponentResult Invoke(GenericReportViewModel model)
{
return View(model);
}
}

Here's the documentation section about synchronous work: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.2#perform-synchronous-work

How do I supress the warning about This async method lacks 'await' operators and will run synchronously when the interface I implement is async?

Ideally you do not want to remove the async keyword, and instead use the asynchronous counterparts of your underlying APIs, so that your code can be non-blocking whenever it is not CPU-bound. For example, as mentioned in comments, you can use connection.OpenAsync() instead of connection.Open():

public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
CancellationToken cancellationToken)
{
using (SqlConnection connection =
new SqlConnection(_configuration.GetConnectionString("PwdrsConnectionRoot")))
{
try
{
await connection.OpenAsync();
}
catch (SqlException)
{
return HealthCheckResult.Healthy();
}
}

return HealthCheckResult.Healthy();
}

However, if you really want to just remove the async keyword and leave your implementation alone, then you'll need to explicitly wrap the return values and exceptions with Task:

private HealthCheckResult CheckHealthImpl(HealthCheckContext context,
CancellationToken cancellationToken)
{
using (SqlConnection connection =
new SqlConnection(_configuration.GetConnectionString("PwdrsConnectionRoot")))
{
try
{
connection.Open();
}
catch (SqlException)
{
return HealthCheckResult.Healthy();
}
}

return HealthCheckResult.Healthy();
}

public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
return Task.FromResult(CheckHealthImpl(context, cancellationToken));
}
catch (Exception e)
{
return Task.FromException(e);
}
}

That should properly implement the interface, and preserve the equivalent behavior to your prior async function.

Suppress warning CS1998: This async method lacks 'await'

I've got an interface with some async functions.

Methods returning Task, I believe. async is an implementation detail, so it can't be applied to interface methods.

Some of the classes that implements the interface does not have anything to await, and some might just throw.

In these cases, you can take advantage of the fact that async is an implementation detail.

If you have nothing to await, then you can just return Task.FromResult:

public Task<int> Success() // note: no "async"
{
... // non-awaiting code
int result = ...;
return Task.FromResult(result);
}

In the case of throwing NotImplementedException, the procedure is a bit more wordy:

public Task<int> Fail() // note: no "async"
{
var tcs = new TaskCompletionSource<int>();
tcs.SetException(new NotImplementedException());
return tcs.Task;
}

If you have a lot of methods throwing NotImplementedException (which itself may indicate that some design-level refactoring would be good), then you could wrap up the wordiness into a helper class:

public static class TaskConstants<TResult>
{
static TaskConstants()
{
var tcs = new TaskCompletionSource<TResult>();
tcs.SetException(new NotImplementedException());
NotImplemented = tcs.Task;
}

public static Task<TResult> NotImplemented { get; private set; }
}

public Task<int> Fail() // note: no "async"
{
return TaskConstants<int>.NotImplemented;
}

The helper class also reduces garbage that the GC would otherwise have to collect, since each method with the same return type can share its Task and NotImplementedException objects.

I have several other "task constant" type examples in my AsyncEx library.



Related Topics



Leave a reply



Submit