简体   繁体   中英

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

I am implementing an interface that defines an asynchronous method - one that returns a Task<T> object.

public class ValidationResult
{
    // Properties that will hold the results of a validation.
}

public interface IValidator
{
    Task<ValidationResult> Validate(object objectToValidate);
}

In most of the classes implementing this interface, there is asynchronous work to do in the Validate method. Therefore, the async and await keywords are leveraged.

public class ExampleAsyncValidator : IValidator
{
    public override async Task<ValidationResult> Validate(object objectToValidate)
    {
        // Perform some asynchronous calls here which will use the await keyword.

        return new ValidationResult { /* ... */ };
    }
}

However, some of the classes implementing this interface have no asynchronous work to do in the Validate method.

public class ExampleSyncValidator : IValidator
{
    public override async Task<ValidationResult> Validate(object objectToValidate)
    {
        // Perform only synchronous calls here. No use of the await keyword.

        return new ValidationResult { /* ... */ };
    }
}

If the async keyword is used for the Validate method in the synchronous scenario above, then I'm getting the CS1998 warning from the compiler.

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

I understand from one particular question and answer that I can simply implement the method without including the async keyword and return completed Task objects.

public class ExampleSyncValidator : IValidator
{
    public override Task<ValidationResult> Validate(object objectToValidate)
    {
        // Perform only synchronous calls here. No use of the await keyword.

        return Task.FromResult(new ValidationResult { /* ... */ });
    }
}

My question is: What is the best practice for handling Exceptions in this scenario?

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<ValidationResult>(ex) ?

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 ; ie, 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 { /* ... */ };
  });
}

As one of the comments points out, the compiler does some work for you in making sure exceptions from async (that use the word await) methods come out in an async manner and further the debugging behavior is affected in how the call stack is preserved. If the method contract returns a task, my recommendation is to let the compiler do that work for you...that is

return await Task.FromResult(...)

Otherwise, you may find yourself on a breakpoint with no stack trace.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM