15.3 Task 语法和语义
15.3.1 声明异步方法和返回类型
1 async static void GetStringAsync() 2 { 3 using (var client = new HttpClient()) 4 { 5 Task<string> task = client.GetStringAsync("https://www.baidu.com/"); 6 string result = await task; 7 } 8 }
15.3.3 可等待模式
大量工作都是通过模式来表示的,这有点类似于 foreach 和LINQ查询。为了更清晰地描述
该模式的轮廓,假设存在一些相关的接口(但实际并没有)。稍后我会介绍真实情况,现在先来
看看虚构的接口:
1 /// <summary> 为包含返回值的异步操作 建立的虚拟的接口 警告:这些并不存在 自定义的</summary> 2 public interface IAwaitable<T> 3 { 4 IAwaiter<T> GetAwaiter(); 5 } 6 7 public interface IAwaiter<T> : INotifyCompletion 8 { 9 bool IsCompleted { get; } 10 T GetResult(); 11 12 //从INotifyCompletion 继承 13 //void OnCompleted(Action continuation); 14 } 15 16 /// <summary> 为没有返回值的异步操作 建立的虚拟的接口 </summary> 17 public interface IAwaitable 18 { 19 IAwaiter GEtAwaiter(); 20 } 21 22 public interface IAwaiter : INotifyCompletion 23 { 24 bool IsCompleted { get; } 25 void GetResult(); 26 27 //从INotifyCompletion 继承 28 //void OnCompleted(Action continuation); 29 }
前面讲述了什么样的表达式可以作为 await 关键字的目标,不过整个表达式本身也同样拥有
一个有趣的类型:如果 GetResult() 返回 void ,那么整个 await 表达式就没有类型,而只是一
个独立的语句。否则,其类型与 GetResult() 的返回类型相同。
例如, Task<TResult>.GetAwaiter() 返回一个 TaskAwaiter<TResult> ,其 GetResult()
方法返回 TResult 。(希望你不会感到奇怪。)根据 await 表达式的类型规则,我们可以编写这样
的代码:
1 using (var client = new HttpClient()) 2 { 3 Task<string> task = client.GetStringAsync("https://www.baidu.com/"); 4 string result = await task; 5 }
这里的 await 表达式不会返回任何类型的值,因此不能将其分配给变量,或作为方法实参进
行传递,也不能执行其他任何将表达式作为值的相关操作。
需要注意的是,由于 Task 和 Task<TResult> 都实现了可等待模式,因此可以在一个异步方
法内调用另一个异步方法:
1 public async Task<int> Foo() 2 { 3 string bar = await BarAsync(); 4 //显然通常会更复杂 5 return bar.Length; 6 } 7 public async Task<string > BarAsync() 8 { 9 //一些异步代码,可能会调用更多异步方法 10 }
组合异步操作正是异步特性大放异彩的一个方面。进入异步模式(mode)后,就可以很轻
松地保持这种模式,编写自然流畅的代码。