Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to add more details to a Task returned by HttpClient.GetAsync

Following code is retrieving content from several url asynchronously, and as soon as one content has been downloaded thanks to Task.WhenAny, then it's processed. But in the processed part, I need the Identifier object. I think it's clearer to show you the code:

 var downloadTasks = new List<Task<string>>();

        foreach (var identifier in input.Identifiers)
        {
            string url = BuildUrl(identifier, input.PeriodInYear, input.Interval);

            var data = _webRequest.GetData(url, token);

            downloadTasks.Add(data); // Here I only add the data, but not the Identifier. I thought about using a List<Tuple<Identifier, Task<string>>, but then I can't use it with Task.WhenAny(...)
        }

        while (downloadTasks.Count > 0)
        {
            var finishedDownloadTask = await Task.WhenAny(downloadTasks);
            downloadTasks.Remove(finishedDownloadTask);

            foreach (var content in await finishedDownloadTask)
            {
                // hereI I also need the Identifier object here !
            }
        }

Here the code of GetData:

public virtual async Task<string> GetData(string uri, CancellationToken token)
    {
        // log removed
        // try catch removed

        string result = string.Empty;

            using (var client = new HttpClient())
            using (var response = await client.GetAsync(uri, token).ConfigureAwait(false))
            {
                if (response.IsSuccessStatusCode)
                    result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                else
                    logger.Error("Unable to retrieve data from the following url: {0} - StatusCode: {1}", uri, response.StatusCode);
            }

        return result;     
    }
like image 453
John Avatar asked May 08 '14 15:05

John


People also ask

What does HttpClient GetAsync return?

The HTTP request is sent out, and HttpClient. GetAsync returns an uncompleted Task .

How to POST data using HttpClient in c#?

C# HttpClient POST form data var url = "https://httpbin.org/post"; using var client = new HttpClient(); var data = new Dictionary<string, string> { {"name", "John Doe"}, {"occupation", "gardener"} }; var res = await client. PostAsync(url, new FormUrlEncodedContent(data)); var content = await res. Content.

What is HttpClient GetAsync?

GetAsync(Uri, HttpCompletionOption) Send a GET request to the specified Uri with an HTTP completion option as an asynchronous operation. GetAsync(Uri, CancellationToken) Send a GET request to the specified Uri with a cancellation token as an asynchronous operation.

What does PostAsync return?

Returns. The task object representing the asynchronous operation.


1 Answers

I don't think that the "build a list of tasks", "await Task.WhenAny", "remove completed task from list" approach is very clean.

I find that my code is usually cleaner when I step back, take a look at the code, and write a new asynchronous method that does the "initial" processing as well as the "postprocessing". With your example, it would look something like this:

async Task GetDataAndPostProcessAsync(Identifier identifier, CancellationToken token)
{
  var url = BuildUrl(identifier, input.PeriodInYear, input.Interval);
  var content = await _webRequest.GetDataAsync(url, token);
  // Use 'content' with 'identifier'
}

...

var tasks = input.Identifiers.Select(identifier =>
    GetDataAndPostProcessAsync(identifier, token)).ToList();
await Task.WhenAll(tasks);

I cover this in more detail in recipe 2.6 of my Concurrency in C# Cookbook.

like image 56
Stephen Cleary Avatar answered Oct 06 '22 00:10

Stephen Cleary