Asynchronous method using .Result

后端 未结 1 1787
一向
一向 2021-01-23 14:11

The following method loops through a list of postdata to make multiple requests for a token, for example each request uses a specific clientID. My question relates to async. I\'

相关标签:
1条回答
  • 2021-01-23 14:39

    Does the use of .Result necessarily make the method synchronous?

    It will make the part which blocks with .Result synchronous, as it synchronously waits for the completion of the Task (If your code doesn't deadlocking, which is highly possible in an environment with a custom SynchronizationContext).

    This method invocation:

    Task<string> firstFinishedTask = await Task.WhenAny(downloadTasks);
    

    Will be asynchronous as it will yield control until the first stream is read and converted to a string. If you already have a method marked as async, simply await that part as well:

    HttpResponseMessage response = await client.PostAsync("/Token", httpContent);
    

    Side Note:

    I think I would of taken a different approach to the problem. Generally, it's the network IO which will consume the most time for this method. If possible and given no limitation, I'd concurrently make those network calls, and then process the results as they come:

    public async Task<string> ReturnDataFromUrlAsync(
                                    List<List<KeyValuePair<string, string>>> listOfPostData)
    {
        var client = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:23081")
        };
    
        var downloadTasks = listOfPostData.Select(postData =>
        {
            var content = new FormUrlEncodedContent(postData);
            return client.PostAsync("/Token", content);
        }).ToList();
    
        var tokenBuilder = new StringBuilder(downloadTasks.Count);
        while (downloadTasks.Count > 0)
        {
            var finishedTask = await Task.WhenAny(downloadTasks);
            downloadTasks.Remove(finishedTask);
            var response = await finishedTask;
    
            if (!response.IsSuccessStatusCode)
                continue;
    
            var token = await response.Content.ReadAsStringAsync();
            tokenBuilder.Append(token);
        }
    
        return tokenBuilder.ToString();
    }
    

    Or since you need all of the results in order to process the the token anyway, you can use Task.WhenAll to wait on all of them to complete:

    public async Task<string> ReturnDataFromUrlAsync(
                                    List<List<KeyValuePair<string, string>>> listOfPostData)
    {
        var client = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:23081")
        };
    
        var downloadTasks = listOfPostData.Select(postData =>
        {
            var content = new FormUrlEncodedContent(postData);
            return client.PostAsync("/Token", content);
        });
    
        HttpResponseMessage[] response = await Task.WhenAll(downloadTasks);
    
        var tokenBuilder = new StringBuilder(response.Length);
        foreach (var element in response.Where(message => message.IsSuccessStatusCode))
        {
            tokenBuilder.Append(await element.Content.ReadAsStringAsync());
        }
        return tokenBuilder.ToString();
    }
    
    0 讨论(0)
提交回复
热议问题