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

I have a interface which exposes some async methods. More specifically it has methods defined which return either Task or Task<T>. I am using the async/await keywords.

I am in the process of implementing this interface. However, in some of these methods this implementation doesn't have anything to await. For that reason I am getting the compiler warning "CS1998: This async method lacks 'await' operators and will run synchronously..."

I understand why I am getting these warnings but am wondering whether I should do anything about them in this context. It feels wrong to ignore compiler warnings.

I know I can fix it by awaiting on Task.Run but that feels wrong for a method that is only doing a few inexpensive operations. It also sounds like it will add unneeded overhead to the execution but then I am also not sure if that is already there because the async keyword is present.

Should I just ignore the warnings or is there a way of working around this that I am not seeing?

7

6 Answers

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.

Elena Rostova

Elena Rostova

Lead Health, Wellness & Medical Journalist

Elena Rostova holds a Master's degree in Public Health Journalism. She covers groundbreaking medical research, holistic wellness trends, mental health awareness, and nutritional science.