异步方法缺少等待

本文关键字:等待 异步方法 | 更新日期: 2023-09-27 18:30:37

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

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;
            }
        }

任何帮助将不胜感激!

异步方法缺少等待

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;
    }
}

然后我们摆脱不必要的任务中任务:

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;
    }
}

然后我们摆脱不必要的尝试/捕获:

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);
}

然后我们将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);
    }
}

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

<小时 />

关于HttpClient的说明。虽然上面的代码将其放入using子句中,但这是因为原始代码构造了一个实例并HttpClient实现了IDisposable

但是,您不应这样做,而应重用HttpClient实例。你可以在这里阅读更多关于这个,你用错了HttpClient,它破坏了你的软件的稳定性

您已将方法标记为async但不使用 await 。您的方法没有理由async。您可以将定义重写为

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

async本质上是说'在这种方法中,我们将await一个asnyc调用'

原因是您没有像现在这样等待任何东西。异步调用结束时的结果。试试这个

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;
    }
}

您不能同时使用 await.Result。这是一个或另一个。 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;
        }
    }

另外,您的Task.Run(...)有点毫无意义,只需使用var webApiResponse = await _webApiClient.GetAsync(requestUri));

你试过这个吗:

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;
    }
}

基于此:

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

我建议你退后一步,试着了解async await试图为你做什么。有一点学习曲线,但它将确保你实际上不会因为使用它而使事情变得更糟。