# C#异步方法返回void和Task的区别
(金庆的专栏 2021.2)
如果异步(async关键字)方法有返回值,返回类型为T时,返回类型必然是 `Task<T>`。
但是如果没有返回值,异步方法的返回类型有2种,一个是返回 Task, 一个是返回 void:
```
public async Task CountDownAsync(int count)
{
for (int i = count; i >= 0; i--)
{
await Task.Delay(1000);
}
}
public async void CountDown(int count)
{
for (int i = count; i >= 0; i--)
{
await Task.Delay(1000);
}
}
```
调用时,如果返回 Task, 但返回值被忽略时,VS 会用绿色波浪线警告:
```
CountDownAsync(3);
~~~~~~~~~~~~~~~~~
```
信息为:
```
(awaitable) Task AsyncExample.CountDownAsync(int count)
Usage:
await CountDownAsync(...);
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
```
中文为:
```
CS4014:由于此调用不会等待,因此在此调用完成之前将会继续执行当前方法。请考虑将"await"运算符应用于调用结果。
```
添加 await 后就正常了:
```
await CountDownAsync(3);
```
如果调用者不是一个异步方法,因为只有在异步方法中才可以使用 await,
或者并不想在此等待,如想同时执行多个 CountDownAsync(),
就不能应用 await 来消除警告。
此时可以改用 void 返回值的版本:
```
void Test()
{
...
CountDown(3);
CountDown(3);
...
}
async void CountDown(int count)
{
for (int i = count; i >= 0; i--)
{
await Task.Delay(1000);
}
}
```
> Never call `async Task` methods without also awaiting on the returned Task. If you don’t want to wait for the async behaviour to complete, you should call an `async void` method instead.
摘自:http://www.stevevermeulen.com/index.php/2017/09/using-async-await-in-unity3d-2017/
CountDown() 可以直接调用 CountDownAsync() 实现:
```
async void CountDown(int count)
{
await CountDownAsync(count);
}
```
使用下划线变量忽略异步方法的返回值也可以消除警告:
```
void Test()
{
...
_ = CountDownAsync(3);
_ = CountDownAsync(3);
...
}
```
但是这样同时也会忽略 CountDownAsync() 中的异常。如以下异常会被忽略。
```
void Test()
{
...
_ = CountDownAsync(3);
...
}
async Task CountDownAsync(int count)
{
for (int i = count; i >= 0; i--)
{
await Task.Delay(1000);
}
throw new Exception();
}
```
如果是调用返回 void 的异步方法,Unity 会报错:
```
Exception: Exception of type 'System.Exception' was thrown.
```
## 对 Async 后缀的说明
```
You could say that the Async suffix convention is to communicate to the API user that the method is awaitable. For a method to be awaitable, it must return Task for a void, or Task<T> for a value-returning method, which means only the latter can be suffixed with Async.
```
摘自:https://stackoverflow.com/questions/15951774
grpc 生成的代码中,异步请求返回了一个 AsyncCall 对象,AsyncCall 实现了 GetAwaiter() 接口:
```
public virtual grpc::AsyncUnaryCall<global::Routeguide.Feature> GetFeatureAsync(global::Routeguide.Point request, ...)
```
可以这样调用并等待:
```
var resp = await client.GetFeatureAsync(req);
```
虽然返回类型不是`Task<>`, 但是可等待,所以添加了 Async 后缀。