异步方法缺少等待

Ran*_*iff 1 c# asynchronous asp.net-web-api

我在这个主题上找到了很多,但似乎没有一个例子/答案能解决我们的问题。我们不断收到警告说下面的方法(和其他类似的方法)缺少等待,但是当我们尝试添加等待时,我们得到“无法等待”(无论我们尝试添加等待的地方)。我们做错了什么?

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;
            }
        }
Run Code Online (Sandbox Code Playgroud)

非常感谢任何帮助!

ang*_*son 5

async 是一个关键字,用于将方法标记为编译器完全重写以支持异步/等待模式的候选方法。

发生的情况是该方法在您在await内部使用关键字的每个点都被拆分。如果您将方法标记为async但不使用await它,则会抱怨您将其标记为不必要,或者您忘记使用await. 所以这就是你得到编译器错误的原因。

现在,下一步是添加await到您的方法中,但这意味着稍微更改它:

  • 仅仅为了运行一个任务而启动另一个任务是一步太多
  • .Result在任务上使用容易出现死锁,这就是await派上用场的地方

所以让我们重写你的方法。首先,我们去掉调用Task.Result并替换为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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们摆脱了不必要的 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们去掉不必要的 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);
}
Run Code Online (Sandbox Code Playgroud)

然后我们将HttpClient对象放入using语句中:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将删除字段,这在进行异步工作时通常是一个坏主意,并使其成为此方法的本地概念。这还需要更改AppendDefaultHeaders方法以接受它应该将标头添加为参数的客户端。