110,538
社区成员
发帖
与我相关
我的任务
分享
var x= GetElementId();
WaitUserClicked(x, callback);
.......
这个代码很干净地“委托callback”是异步回调,逻辑上并不阻塞地等到callback被执行才执行 .......代码,而是可能先执行 .......代码。
而 async/await 语法则只能完成上述前2句(不能直接实现前3句),它硬要让你把 callback 写成跟 GeetElementId() 同步顺序执行一模一样的语法,但是你要自己用脑子去把 await 再理解为回调,因为你使用 async/await 语法时你会看到对应于 ......的那部分是提前执行了、并没有被callback所阻塞,这时候你就反而疑惑了。
这就是 async/await 语法很不好的地方。
但是它有什么好处呢?假设有连缀的一堆异步回调,例如a(()=>{
b(x=>{
c(x+1, ()=>{
d(y=>{
f(y);
};
});
});
});
这样的代码就看上去很垃圾,那么使用 async/await 就比较清爽。
其实用宰牛刀杀鸡,如果在一个群体成了习惯,那么可能这个群体也不知道还有轻量级的刀子了。
private void btn_test_Click(object sender, RoutedEventArgs e)
{
Task<string> t = doAsyncWork();
t.ContinueWith(WhenTaskFinished, TaskScheduler.FromCurrentSynchronizationContext()); // 1,2
} // 3
private void WhenTaskFinished(Task<string> task) // 4
{
try
{
btn_test.Content = task.Result;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
你可以观察到:
1、await就是一种ContinueWith,它登记一个’回调函数‘,这个回调函数(即那个WhenTaskFinished)将在Task完成后得到调用。
2、await同时也捕获了当前的同步环境,即那个TaskScheduler.FromCurrentSynchronizationContext,
并只在捕获的同步环境下运行’回调函数‘。
3、await马上返回。
因为await启动task后马上返回UI,因此UI不会被阻塞。
因为await捕获的同步环境就是UI,因此回调将在UI线程上执行,而不用麻烦Dispatcher.Invoke来同步到UI。
btn_test.Content = doCallback(delegate
{
...............这里是 await 之前的代码
doAsyncWork();
},
resultValue =>
{
..........这里是await之后的代码
MessageBox.Show("haha");
});
这里所谓的“用顺序流程的语法来写一个异步多线程程序”是个障眼法,它其实是专门制造假象的。
实际上,btn_test_Click 方法在执行到 await 的地方就已经结束了,后边的都是回调执行代码。所以语法 async 放到方法修饰中,不让你调试到 btn_test_Click 方法结束部分。以此让程序员看不清楚回调的本意。