Synchronous async method implementation

April 9th 2021 Async C#

Interfaces usually have methods that return tasks because they were designed to be asynchronous. But how should you implement such a method when you don't need to call any asynchronous methods inside it?

Let's say you have the following interface:

public interface IProcessor
{
  Task Process();
}

When you need to call other asynchronous methods, you will implement the method above as async:

public async Task Process()
{
  // ...
  await MyAsyncMethod();
  // ...
}

You can do the same even if you don't need to call any asynchronous methods:

public async Task Process()
{
  // ...
  MySyncMethod();
  // ...
}

But you will receive a compiler warning:

CS1998: 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. SynchronousAsync.

Despite that, the code will compile and work correctly. However, there's a better way to implement such a method without making it async:

public Task Process()
{
  // ...
  return Task.CompletedTask;
}

The calling code will still be able to await this method, but since the task returned by the Task.CompletedTask property is already in the completed state, the method will execute synchronously without ever yielding the thread.

A similar approach can be used for methods that return a value:

public Task<bool> ProcessWithResult()
{
  return Task.FromResult(true);
}

The Task.FromResult() method will return a completed task with the parameter value as its result.

If you want to throw an exception from such a method, you should call the Task.FromException() method:

public Task Process()
{
  return Task.FromException(new InvalidOperationException());
}

That's a better alternative to simply throwing the exception because there's a slight difference in behavior between the two implementations:

  • When throwing the exception, the method will throw when you call it even if you don't await it:

    var task = processor.Process(); // throws
    
  • When returning Task.FromException() instead, the exception will only be thrown when the caller awaits the returned task:

    var task = processor.Process();
    await task; // throws
    

You can try all of the above with the code in my GitHub repository.

There's usually no need for synchronous methods to return tasks. But when you're implementing an interface, you don't have a choice. If methods return tasks because they could be asynchronous, you need to return them even when you're implementing them synchronously. Although you could still make such a method async it's better to keep it synchronous and return tasks explicitly.

If you're looking for online one-on-one mentorship on a related topic, you can find me on Codementor.
If you need a team of experienced software engineers to help you with a project, contact us at Razum.
Copyright
Creative Commons License