Synchroon werken met Tasks

Wanneer je interfaces ontwerpt leg je in de interface vaak al vast of deze asynchroon werkt of synchroon werkt. Neem de volgende volgende Task Async Pattern implementatie, het patroon dat voor de keywords async en await word gebruikt:

public interface ITAPProvider
{
    Task<string> GetStringFromUrl(string url)
} 

 Een mogelijke implementatie die daar bij hoort kan er als volgt uitzien:

public class HttpClientProvider : ITAPProvider
{
    public async Task<string> GetStringFromUrl(string url)
    {
       HttpClient client = new HttpClient();
       string result = await client.GetStringAsync(url);
       return result;
    }
}

Wat interessant is in deze code dat er niet een Task<string> word gereturneed maar een string. De compiler pakt dit zelf in een Task. Dit gebeurd omdat er de keyword async in de method staat.
Wanneer je nu geen await gebruikt in de body gaat de compiler echter waarschuwingen geven. Neem deze code:

public class HttpClientProvider : ITAPProvider
{
   public async Task<string> GetStringFromUrl(string url)
   {
      return "test";
   }
}

Levert deze compiler warning op:

Nu kan je deze negeren, maar zelf vind ik dat niet erg fijn. Nu kan je ook het async keyword weghalen en zelf een task terug geven 

public class HttpClientProvider : ITAPProvider
{
   public Task<string> GetStringFromUrl(string url)
   {
            return Task.Run(() => "test");
   }
}

Echter word met deze code de string return echt weer async en een hoop overbodige overhead toegevoegd wat de code traag maakt. Gelukkig is er ook een andere oplossing. Er is een class  TaskCompletionSource en hiermee kan je een string inpakken in een task wat zonder al te veel overhead gebeurd (er gaat namelijk geen async actie lopen). Dat zou er zo uitzien:

public Task<string> GetStringFromUrl(string url)
{
    return Task.FromResult("test");
}

Wanneer je toch niet helemaal direct het resultaat hebt maar niet met await kan werken is er nog een andere oplossing:

public Task<string> GetStringFromUrl(string url)
{
    TaskCompletionSource<string> taskResult = new TaskCompletionSource<string>();
    taskResult.SetResult("test");
    return taskResult.Task;

 

 Nu ben je van de compiler warning af en introduceer je geen extra overhead door gebruik van een losse task