Search code examples
c#asynchronoustaskhttpclientresponse

Is this method asynchronous?


This method uses .Result. Does that mean the tasks will not run asynchronously? Does it require refactoring to enable each request for a token to be truly async?

public async Task<string> ReturnDataFromUrlAsync(List<List<KeyValuePair<string, string>>> listOfPostData)
{
    string allTokens = "";

    var client = new HttpClient
    {
        BaseAddress = new Uri("http://localhost:23081")
    };

    var downloadTasksQuery = listOfPostData.Select(postData =>
    {
        var content = new FormUrlEncodedContent(postData);
        HttpResponseMessage response = client.PostAsync("/Token", content).Result;                                 
        var responseBodyAsText = response.Content.ReadAsStringAsync();
        return responseBodyAsText;               
    });

    List<Task<string>> downloadTasks = downloadTasksQuery.ToList();   

    while (downloadTasks.Count > 0)
    {
        Task<string> firstFinishedTask = await Task.WhenAny(downloadTasks);

        downloadTasks.Remove(firstFinishedTask);

        // Await the completed task. 
        string content = await firstFinishedTask;

        allTokens = allTokens + content;
    }

    return allTokens;
}

Solution

  • So at the moment this call:

    HttpResponseMessage response = client.PostAsync("/Token", content).Result;
    

    Will cause the calling thread to stall and wait for the result. If you place the await key word before client.PostAsync it will cause the calling thread to return to the caller and resume once the operation has completed. As this is being done in a linq query you will need to make the lambda expression asynchronous like so:

    var downloadTasksQuery = listOfPostData.Select(async postData =>
        {
            var content = new FormUrlEncodedContent(postData);
            HttpResponseMessage response = await client.PostAsync("/Token", content);                               
            var responseBodyAsText = await response.Content.ReadAsStringAsync();
            return responseBodyAsText;               
        });
    

    Appending the async keyword in a lambda expression will infer that the return type must be of type Task, and as we are are also selecting strings it will of type IEnumerable<Task<string>>

    I also noticed this:

    var responseBodyAsText = response.Content.ReadAsStringAsync();
    

    I think the reason for the confusion is because this statement is actually returning a Task not a string (be careful what you name stuff if you are using var)

    Once again we can just place the await keyword before the response.Content and the return key will be inferred automatically. Then we can select strings, in the linq query rather than Task<string>