I need to subscribe for updates from 3rd party system using provided async-unfriendly library ( they will not support async in the near future). The simplified method for subscription expects delegate Action<float> onTemperatureChange:
// Third party library class WeatherServer { // I cannot change signature of this method (it is third party) void SubscribeTemperature(Action<float> onTemperatureChange); } I can easily work with such API using synchronous method:
void onTemperatureChange(float newTemperature) { // Synchronous temperature handling } weatherServer.SubscribeTemperature(onTemperatureChange); What is the recommended approach if I need to have asynchronous temperature handler like this:
async Task onTemperatureChangeAsync(float newTemperature) { var feelsLikeTemperature = await anotherLibrary1.GetFeelsLikeTemperature(newTemperature); await anotherLibrary1.WriteTemperature(feelsLikeTemperature); } Please note that above example is heavily simplified and internal handler logic is complex logic full of awaited async methods.
Considering I cannot change class WeatherServer and I have to live with subscription via Action<float> onTemperatureChange, I have explored following options:
Change handler return type to async void
async void onTemperatureChangeAsync(float newTemperature), but it complicates unit testing and exceptions:// async void async void onTemperatureChangeAsync(float newTemperature) { // asynchronous temperature handling (for example): var feelsLikeTemperature = await anotherLibrary1.GetFeelsLikeTemperature(newTemperature); await anotherLibrary1.WriteTemperature(feelsLikeTemperature); }Change handler to sync method using
Resulton internal async call,but.Result()is a bad practice:void onTemperatureChangeAsync(float newTemperature) { // Result() is a bad practice anotherLibrary.WriteTemperature(newTemperature).Result(); }
Since option 2 using .Result() can cause significant issues, I assume that I have to choose with async void.
Is there any other way ? I assume that I am not first one, who needs to solve such issue during migration to async world.
Edited: added more awaited calls to async void to demonstrate why await is needed inside handler (even if async void cannot be awaited) and removed wrong comment, that async void is completely bad practice as pointed by answers.
TaskwithWaitorGetAwaiter().GetResult()(not.Result, which is truly bad practice) has no benefits. You'll be able to putawaits inside it, but handler will still wait for its completion synchroniously.async voidis not bad practice, it's just often used thoughtlessly where not supposed to be used. But event handlers (or delegates) is one of not so much cases, when it is applicable.async void. But when you makeasync voidconnection to database followed by reading something with it - here comes a trouble.async voidwon't be a problem.