Assume I have an interface method implemented as
public void DoSomething(User user) { if (user.Gold > 1000) ChatManager.Send(user, "You are rich: " + user.Gold); }
After some time I realize that I want to change it:
public async Task DoSomething(User user) { if (user.Gold > 1000) ChatManager.Send(user, "You are rich: " + user.Gold); if (!user.HasReward) { using(var dbConnection = await DbPool.OpenConnectionAsync()) { await dbConnection.Update(user, u => { u.HasReward = true; u.Gold += 1000; }); } } }
I'm changing the method signature in the interface. But the calling methods were synchronous and I have to make not only them async but also the whole call tree async.
Example:
void A() { _peer.SendResponse("Ping: " + _x.B()); } double X.B() { return _someCollection.Where(item => y.C(item)).Average(); } bool Y.C(int item) { // ... _z.DoSomething(); return _state.IsCorrect; }
should be changed to
async void AAsync() { _peer.SendResponse("Ping: " + await _x.BAsync()); } async Task<double> X.BAsync() { // await thing breaks LINQ! var els = new List<int>(); foreach (var el in _someCollection) { if (await y.CAsync(item)) els.Add(el); } return _els.Average(); } async Task<bool> Y.CAsync(int item) { // ... await _z.DoSomething(); return _state.IsCorrect; }
The affected call tree may be very big (many systems and interfaces) so this change is hard to do.
Also when the first A
method is called from interface method like IDisposable.Dispose
- I can't make it async.
Another example: imagine that multiple calls to A
were stored as delegates. Previously they were just called with _combinedDelegate.Invoke()
but now I should go through GetInvocationList()
and await
on each item.
Oh, and consider also replacing property getter with async method.
I can't use Task.Wait()
or .Result
because:
ThreadPool
threads in server appThreadPool
threads are Wait
ing there are no threads to complete any task.So the question is: should I make absolutely all my methods initially async
even if I'm not planning to call anything async inside? Won't it hurt performance? Or how to design things to avoid such hard refactorings?
If a method has no async operations inside it there's no benefit in making it async . You should only have async methods where you have an async operation (I/O, DB, etc.). If your application has a lot of these I/O methods and they spread throughout your code base, that's not a bad thing.
By convention, you append "Async" to the names of methods that have an async modifier. You can ignore the convention where an event, base class, or interface contract suggests a different name. For example, you shouldn't rename common event handlers, such as Button1_Click .
If a method is declared async, make sure there is an await! If your code does not have an await in its body, the compiler will generate a warning but the state machine will be created nevertheless, adding unnecessary overhead for an operation that will actually never yield.
In computer programming, the async/await pattern is a syntactic feature of many programming languages that allows an asynchronous, non-blocking function to be structured in a way similar to an ordinary synchronous function.
should I make absolutely all my methods initially async even if I'm not planning to call anything async inside?
This design problem with async
is essentially the same as the problem with IDisposable
. That is, interfaces have to predict their implementations. And this is going to be messy, no matter what you do.
In my experience, it's usually rather straightforward to consider a method/interface/class and predict whether it will use I/O or not. If it requires I/O, then it should probably be made task-returning. Sometimes (but not always), it's possible to structure your code so that I/O is done in its own section of the code, leaving business objects and logic strictly synchronous. The Redux pattern in the JavaScript world is a good example of this.
But bottom line, sometimes you make the wrong call and have to refactor. I think this is a far better approach than just making every method asynchronous. Do you make every interface inherit from IDisposable
and use using
everywhere? Nah, you only add it when necessary; and you should take the same approach with async
.
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