Suppress Warning Cs1998: This Async Method Lacks 'Await'

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.

How to properly resolve warning CS1998: This async method lacks 'await' (without surpressing it)?

If there's no await, remove the async keyword. async doesn't make a method asynchronous. It's not part of the method signature. It's just syntactic sugar that allows the use of await to await an already executing asynchronous operation without blocking.

In your test method create an already completed task with Task.FromResult and return it as-is:

public Task<DataModel> GetData()
{
var model=new DataModel();
//Add some test data
return Task.FromResult(model);
}

Warning CS1998 This async method lacks 'await' operator, correct way to silence?

removing Task as result is not an option, it is typically an interface (that I cannot control) implementation

Using the async keyword creates a state machine, which is unnecessary if the implementation is synchronous.

If a method must return Task<TResult> to satisfy an interface contract, use Task.FromResult().

In the case of a method that returns a non-generic Task, return Task.CompletedTask.


In the case of exception handling, use Task.FromException:

public static Task<int> Test3(bool err)
{
if (err) return Task.FromException<int>(new InvalidOperationException());

try { return Task.FromResult(GetNum(42)); }
catch (Exception e) { return Task.FromException<int>(e); }
}

If the amount of boilerplate is an issue then how about using these wrappers:

Task RunAsTask(Action action)
{
try { action(); }
catch (Exception e) { return Task.FromException(e); }
return Task.CompletedTask;
}

Task<TResult> RunAsTask<TResult>(Func<TResult> func)
{
try { return Task.FromResult(func()); }
catch (Exception e) { return Task.FromException<TResult>(e); }
}

Then you can write your methods synchronously:

public static Task<int> Test1(bool err) => RunAsTask(() =>
{
if (err)
throw new InvalidOperationException();
return GetNum(42);
});

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();
}

Remove the async/await warning CS1998, when implementing interface

I believe this is just a case of a tired Visual Studio and 1) a recompile or 2) VS restart will fix the issue.

What is the reason behind CS1998 method lacks await operators

What are the reasons behind the warning?

Simply put, an async method that does not use await is almost certainly wrong. Not always wrong, or this would be an error. But almost always wrong, hence the warning.

An incredibly common async-newbie mistake is to assume async means "make this method asynchronous". This is commonly paired with the assumption that "asynchronous" means "run on a background thread", but sometimes it's just an assumption of "magic".

Thus, the warning explicitly points out that the code will run synchronously.

I have also found this warning helpful when refactoring my own code - sometimes I end up with an async method that should be changed to a synchronous method, and this warning points that out.

It's true that async without await could be useful to reduce code if you have non-trivial (i.e., possibly exception-generating) synchronous code and you need to implement an asynchronous method signature. In that case, you can use async to avoid a half-dozen lines of TaskCompletionSource<T> and try/catch code. But this is an extremely small use case; the vast majority of the time, the warning is helpful.

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.

How do I handle Exceptions when resolving warning CS1998: This async method lacks 'await' operators

If my synchronous code throws an Exception, should I let it fall through to the caller untouched? Or, would it be better to catch it and wrap it in a failed Task object using Task.FromException(ex)?

You should place it on the returned Task; i.e., use Task.FromException.

public override Task<ValidationResult> Validate(object objectToValidate)
{
try
{
// Perform only synchronous calls here. No use of the await keyword.
return Task.FromResult(new ValidationResult { /* ... */ });
}
catch (Exception ex)
{
return Task.FromException<ValidationResult>(ex);
}
}

Alternatively, you can use async without await and suppress the warning with a #pragma:

#pragma warning disable 1998
public override async Task<ValidationResult> Validate(object objectToValidate)
#pragma warning restore 1998
{
// Perform only synchronous calls here. No use of the await keyword.
return new ValidationResult { /* ... */ };
}

If you do this a lot, you can look into creating a helper method. This one is part of Nito.AsyncEx:

public static class TaskHelper
{
#pragma warning disable 1998
public static async Task ExecuteAsTask(Action func)
#pragma warning restore 1998
{
func();
}

#pragma warning disable 1998
public static async Task<T> ExecuteAsTask<T>(Func<T> func)
#pragma warning restore 1998
{
return func();
}
}

So if you install Nito.AsyncEx or include the code above in your project, then you can use the ExecuteAsTask method as such:

using static Nito.AsyncEx.TaskHelper;
...
public override Task<ValidationResult> Validate(object objectToValidate)
{
return ExecuteAsTask(() => {
// Perform only synchronous calls here. No use of the await keyword.
return new ValidationResult { /* ... */ };
});
}

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();
}); //
} // }

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.



Related Topics



Leave a reply



Submit