2

Based on the answer of another question: HttpClient async requests not completing for large batch sent out in a loop

I'm using the extension method and applying the logic they propose to use timeout with HttpClient class and avoid hangs or no response from it.

 public static Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout)
 {
      var delay = task.ContinueWith(t => t.Result
          , new CancellationTokenSource(timeout).Token);
      return Task.WhenAny(task, delay).Unwrap();
 }

So, calling HttpClient like this should prevent any "Tasks gone bad" from never ending:

  Task<HttpResponseMessage> _response = httpClient.PostAsJsonAsync<Object>(Target, new Object()).WithTimeout<HttpResponseMessage>(httpClient.Timeout);

Now, when applying this, using a Task array populated with a loop, I'd hope that all tasks run in parallel and finish in the specified timeout value. Just for showing a sample, I'm setting the timeout in 400ms, however the next line after the Tasks.WhenAll(tasks) line is hit around 20 seconds after.

public async Task<JObject> GetResponse(JObject request, TimeSpan timeout)
    {   
            Timespan timeout = Timespan.FromMilliseconds(400);  
            HttpClient httpClient = new HttpClient();
            HttpResponseMessage response = await httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endpoint), localRequest.ToString()).WithTimeout<HttpResponseMessage>(timeout);
            return await response.Content.ReadAsAsync<JObject>().WithTimeout<JObject>(timeout); 
    }

Calling the async method:

        Task<JObject>[] tasks = new Task<JObject>[totalResultCounter];

        foreach (JObject request in anArray)
        {
            tasks[counter] = GetResponse(request);
        }

        await Task.WhenAll(tasks);
        MyNextMethod();

Wouldn't it be expected that tasks complete in 500 ms and next line is hit? Why it takes around 20 secs in reach the MyNextMethod line? Maybe I'm doing anything wrong when setting the timeout in PostAsJsonAsync/ReadAsAsync calls?

The timeout extension method works fine isolated.

When I use the keyword await, it seems it waits to complete, however not using the timeout defined:

HttpResponseMessage response = await httpClient.PostAsJsonAsync(string.Format("{0}api/GetSupplierResponse", endpoint), localRequest.ToString()).WithTimeout(timeout);

If I change it to:

 Task<HttpResponseMessage> response = httpClient.PostAsJsonAsync<string>(string.Format("{0}api/GetResponse", endpoint), localRequest.ToString()).WithTimeout<HttpResponseMessage>(timeout);

it is fast.

But then how I get the result? I need to use an await for the ReadAsAsync method:

 JObject result = await response.Content.ReadAsAsync<JObject>().WithTimeout<JObject>(timeout);

Once I use it, it takes a lot of time, 20 secs to complete my loop of around 200 items.

PostAsJsonAsync and ReadAsAsync should be handled as independent tasks that won't respect the timeout specified in httpClient? Or the use of these methods takes more than 500ms and that's why it takes a lot when using a loop?

Any help/conceptual clarification will be appreciated.

Community
  • 1
  • 1
Alberto Montellano
  • 5,886
  • 7
  • 37
  • 53
  • 1
    There's no reason for your `WithTimeout` method to have the `WhenAny`. You can just return the task returned from `ContinueWith`. It'll already function as you want it to. – Servy Mar 17 '15 at 14:58
  • 3
    How about using `HttpClient.Timeout` property? – Sriram Sakthivel Mar 17 '15 at 14:59
  • @SriramSakthivel the referenced question at the begining of the post explains why can't use HttpClient.Timeout, HttpClient async requests not completing for large batch sent out in a loop – Alberto Montellano Mar 17 '15 at 15:40
  • @Servy I tested what you mention, but nothing changes, it is still taking around 20 secs to hit the next line after Tasks.WaitAll , or they are not running in parallel maybe? – Alberto Montellano Mar 17 '15 at 15:41
  • @AlbertoMontellano That method works fine. Test it in isolation. Your problem is elsewhere. – Servy Mar 17 '15 at 15:43
  • @Servy when you mention in isolation, you mean working with HttpClient PostAsJsonAsync/ReadAsAsync calls ? or not calling them ? Thanks for your feedback. – Alberto Montellano Mar 17 '15 at 15:45
  • 1
    @AlbertoMontellano When I say test in isolation I mean test each piece separately, rather than testing the whole thing and having no idea which of the pieces isn't doing what you think it is. Test the method you wrote, see if it actually functions as you intended. Test the business logic you have and see if it's functioning as you intended. Break the logic into smaller pieces, and make sure each of them are independently doing what you expect them to do. – Servy Mar 17 '15 at 15:48
  • Check with this. httpClient.Timeout = TimeSpan.FromMilliseconds(1000); //Manage m.secs – dLcreations Jan 12 '18 at 07:52

0 Answers0