网站首页 文章专栏 深入理解 await 编程范式
- async 关键字只是告诉编译器,这个函数内部可以有 await 调用,需要把它编译为状态机 - await 的对象既不是函数,也不是Task,而是一个符合 `IAwaitable` 接口描述的鸭子类型(实际上并没有这个接口存在) 不直接用接口的好处是,只用拓展方法就可以把一个对象拓展成 awaitable 对象,对代码没有侵入性
public class Program
{
private static async Task Main(string[] args)
{
// 异步发起一个网络请求
var http = new HttpClient();
var txt = await http.GetStringAsync("https://www.bing.com");
Console.WriteLine(txt);
}
}
async 关键字只是告诉编译器,这个函数内部可以有 await 调用,需要把它编译为状态机
await 的对象既不是函数,也不是Task,而是一个符合 IAwaitable
接口描述的鸭子类型(实际上并没有这个接口存在)
不直接用接口的好处是,只用拓展方法就可以把一个对象拓展成 awaitable 对象,对代码没有侵入性
public interface IAwaitable<T>
{
IAwaiter<T> GetAwaiter();
}
public interface IAwaiter<T>
{
bool IsCompleted;
void OnCompleted(Action continuation);
T GetResult();
}
在 Rider 中可以看到 await 的对象需要实现哪些方法
所以
async void MethodAsync() { }
是不能被 await 的,因为它返回的对象是 void
public class ButtonAwaiter : INotifyCompletion
{
public bool IsCompleted { get; private set; }
private Button m_Btn;
private Action m_Continuation;
public ButtonAwaiter(Button btn)
{
m_Btn = btn;
}
public void OnCompleted(Action continuation)
{
m_Continuation = continuation;
m_Btn.onClick.AddListener(OnClickInternal);
}
public void GetResult() { }
private void OnClickInternal()
{
m_Btn.onClick.RemoveListener(OnClickInternal);
m_Continuation?.Invoke();
m_Continuation = null;
IsCompleted = true;
}
}
public static class ButtonEx
{
public static ButtonAwaiter GetAwaiter(this Button self) => new ButtonAwaiter(self);
}
// 拓展后使用方式
private async void Start()
{
var btn = GetComponent<Button>();
await btn;
Debug.Log("Button Click");
}
IAsyncStateMachine
对象(这个对象本身不可定制,debug编译时这个对象是class,release编译这个对象是struct)AsyncTaskMethodBuilder
把 MoveNext() 方法注册为 awaiter 完成时的回调[AsyncMethodBuilder]
属性,可以指定它的 builder