简体   繁体   中英

The async method lacks await

I've found a lot on this topic but none of the examples/answers seem to address our issue. We keep getting warnings saying the method below (and others like it) lack await, but when we try to add await we get "can't await this" (wherever we try to add the await). What are we doing wrong?

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
        {
            try
            {
                _webApiClient = new HttpClient { BaseAddress = _baseAddress };
                if (appendHeader) AppendDefaultHeaders();
                var webApiResponse = Task.Run(() => _webApiClient.GetAsync(requestUri)).Result;
                var responseContent = webApiResponse.Content.ReadAsStringAsync().Result;
                return JsonConvert.DeserializeObject<T>(responseContent);
            } 
            catch (Exception ex) {
                throw ex;
            }
        }

any assistance is greatly appreciated!

You've marked your method async but you are not using await . There is no reason for your method to be async . You can rewrite the definition to be

protected T PerformGet<T>(string requestUri, bool appendHeader)

async essentially says 'In this method, we're going to await an asnyc call'

async is a keyword that marks a method as a candidate for a complete rewrite by the compiler to support the async/await pattern.

What happens is that the method is split at every point where you use the await keyword internally. If you mark a method as async but don't use await it will complain as either you marked it unnecessary, or you forgot to use await . So that's why you get the compiler errors.

Now, the next bit is to add await to your method, but that means changing it slightly:

  • Spinning up another task just to run a task is one step too many
  • Using .Result on tasks are prone to deadlocks, and this is where await comes in handy

So let's rewrite your method. First we get rid of calls to Task.Result and replace with await :

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
    try
    {
        _webApiClient = new HttpClient { BaseAddress = _baseAddress };
        if (appendHeader) AppendDefaultHeaders();
        var webApiResponse = await Task.Run(() => _webApiClient.GetAsync(requestUri));
        var responseContent = await webApiResponse.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    } 
    catch (Exception ex) {
        throw ex;
    }
}

Then we get rid of the unnecessary task-in-task:

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
    try
    {
        _webApiClient = new HttpClient { BaseAddress = _baseAddress };
        if (appendHeader) AppendDefaultHeaders();
        var webApiResponse = await _webApiClient.GetAsync(requestUri);
        var responseContent = await webApiResponse.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    } 
    catch (Exception ex) {
        throw ex;
    }
}

Then we get rid of the unnecessary try/catch:

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
    _webApiClient = new HttpClient { BaseAddress = _baseAddress };
    if (appendHeader) AppendDefaultHeaders();
    var webApiResponse = await _webApiClient.GetAsync(requestUri);
    var responseContent = await webApiResponse.Content.ReadAsStringAsync();
    return JsonConvert.DeserializeObject<T>(responseContent);
}

Then we place the HttpClient object into a using statement:

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
    using (var client = new HttpClient { BaseAddress = _baseAddress })
    {
        if (appendHeader) AppendDefaultHeaders(client);
        var response = await client.GetAsync(requestUri);
        var responseContent = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    }
}

This removes the field, which is usually a bad idea when doing asynchronous work, and makes it a local concept of this method. This also requires a change to the AppendDefaultHeaders method to accept the client that it should add the headers to as a parameter.

The reason is you are not awaiting anything as you have .Result at the end of your async calls. Try this

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)    
{
    try
    {
        _webApiClient = new HttpClient { BaseAddress = _baseAddress };
        if (appendHeader) AppendDefaultHeaders();
        var webApiResponse = await Task.Run(() => _webApiClient.GetAsync(requestUri));
        var responseContent = await webApiResponse.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    } 
    catch (Exception ex) {
        throw ex;
    }
}

You can't use await and .Result at the same time. It's one or another. Result will block your thread until the async method is finished, like it would be a synchronous method. await indicates the thread will be moved back to the threadpool while the method is running, and when the method has finished, it will grab another thread to continue the rest of the function. So your function should be:

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
    {
        try
        {
            _webApiClient = new HttpClient { BaseAddress = _baseAddress };
            if (appendHeader) AppendDefaultHeaders();
            var webApiResponse = await Task.Run(() => _webApiClient.GetAsync(requestUri));
            var responseContent = await webApiResponse.Content.ReadAsStringAsync().;
            return JsonConvert.DeserializeObject<T>(responseContent);
        } 
        catch (Exception ex) {
            throw ex;
        }
    }

Also your Task.Run(...) is kinda pointless, just use var webApiResponse = await _webApiClient.GetAsync(requestUri));

Have you tried this:

protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
    try
    {
        _webApiClient = new HttpClient { BaseAddress = _baseAddress };
        if (appendHeader) AppendDefaultHeaders();
        var webApiResponse = await Task.Run(() => _webApiClient.GetAsync(requestUri));
        var responseContent = await webApiResponse.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    } 
    catch (Exception ex) {
        throw ex;
    }
}

Based on this:

var webApiResponse = Task.Run(() => _webApiClient.GetAsync(requestUri)).Result;

I would suggest you take a step back and try to understand what async await is trying to do for you. There is a bit of a learning curve, but it will ensure you don't actually make things worse by using it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM