9

Application

I have a View Model that is referenced by multiple projects or Views.

Due to the API, some of the View projects are async and others are not.

The View project injects it's implementation of an interface into the View Model using an IOC container.

Code

Consider this minimal example of the code

The interface:

public interface IDatabase
{
    Task<Data> GetData();
}

The View Model awaits the GetData method:

public class DisplayDataViewModel
{
     private readonly  IDatabase _database
     public DisplayDataViewModel(IDatabase database)
     {
          _database = database;
     }

     private async Task GetDataFromDatabase()
     {
         Data data = await _database.GetData();
         // do something with data...
     }
}

Asynchronous View project uses an Async function to get the data

public class AsynchronousViewDataBase : IDatabase
{
    public async Task<Data> GetData()
    {
        return await GetDataAsync();
    }
}

Synchronous View project uses a synchronous call to get the data, but due to the constraint of the interface, I need to return a Task, but I am merely interested in the return of the Data thus making the method async.

public class SynchronousViewDatabase : IDatabase
{
    public async Task<Data> GetData()
    {
        return GetData();
    } 
}

But then it gives warning:

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.

Which is sort-of fine with me, since I don't care if it runs synchronously.

Using a Task.Run to suppress the warning seems unnecessary since the call can (and in my case does) originate from a background thread.

Question

Is there a best practice to implement this type of non-await async method? Is it okay to ignore the warning? Is there a better way to implement the async Synchronous call?

Additional

I did convert the interface to a synchronous call, and using the Task.Result in the Asynchronous View (which fixed the warning), but that caused the app to stop responding and just a lot of difficult issues.

Note: I am not stating that using the async keyword magically makes the method run asynchronously.

Community
  • 1
  • 1
Barnstokkr
  • 2,904
  • 1
  • 19
  • 34

3 Answers3

11

async is an implementation detail so you can remove it from the method if you are not using await, then you can use Task.FromResult to return an already completed task with the required result.

public class SynchronousViewDatabase : IDatabase
{
    public Task<Data> GetData()
    {
        return Task.FromResult<Data>(GetData());
    } 
}
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61
  • Given that GetData in FromResult returns Data and not Task. The askers example seem to be a bit blurry. Upvoted this answer. – BoeseB Feb 10 '15 at 11:25
6

Is there a best practice to implement this type of non-await async method?

Yes, its called a synchronous API. Don't force code which isn't naturally async to be so. Add a GetData method which returns a string.

Is it okay to ignore the warning?

It's ok as long as you understand the implications. Calling a synchronous method which may be long running from an async method will probably surprise the consumers of this method as it will actually block. Also, there's the fact that any exception will be encapsulated via the state-machines generated Task. And of course the small overhead of the state machine itself and any lifted operators.

Is there a better way to implement the async Synchronous call?

It really depends on the use-case. If this was a short operation which isn't noticeable, you can get away with using Task.FromResult and removing the async modifier (as others said) or deferring the call to a thread pool thread. From your question i understand you want neither.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
4

Your interface should implement two different Methods and let the caller decide if he needs it async or sync

Data GetData();

and

Task<Data> GetDataAsync();

here is a possible way to run your async method synchron

public class SynchronousViewDatabase : IDatabase
{
    public Data GetData()
    {
        var getDataTask = GetDataAsync();
        getDataTask.RunSynchroniously();
        if(getDataTask.Status == TaskStatus.RanToCompletion)
           return getDataTask.Result;

        throw getDataTask.Exception;
    } 
}
BoeseB
  • 695
  • 4
  • 17