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.
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.
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?
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.
Marking a function as async without using await or returning a value inside it can lead to an unintended promise return and a larger transpiled output. Often the function can be synchronous and the async keyword is there by mistake.
The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.
Error handlingA try/catch block can be used to handle asynchronous errors in an async function. Alluding to the fact that an async function always return a Promise, one can opt to use a . catch() in place of a whole try/catch block.
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());
}
}
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.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With