异步方法缺少等待
本文关键字:等待 异步方法 | 更新日期: 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
试图为你做什么。有一点学习曲线,但它将确保你实际上不会因为使用它而使事情变得更糟。