原答案:
据我了解,您的问题是“如果不是专门为基于任务的异步实现“等待”,而是实现了更通用的 call-with-current-continuation 控制流操作,该怎么办?
好吧,首先让我们想想“等待”是做什么的。"await" 接受一个类型的表达式Task<T>
,获得一个等待者,并用当前的延续调用等待者:
await FooAsync()
变得有效
var task = FooAsync();
var awaiter = task.GetAwaiter();
awaiter.BeginAwait(somehow get the current continuation);
现在假设我们有一个操作符callcc
,它接受一个方法作为它的参数,并使用当前的延续调用该方法。看起来像这样:
var task = FooAsync();
var awaiter = task.GetAwaiter();
callcc awaiter.BeginAwait;
换句话说:
await FooAsync()
无非就是
callcc FooAsync().GetAwaiter().BeginAwait;
这是否回答你的问题?
更新#1:
正如评论者指出的那样,下面的答案假定来自 async/await 功能的“技术预览”版本的代码生成模式。实际上,我们在该功能的 beta 版本中生成的代码略有不同,尽管逻辑上是相同的。目前的代码生成类似于:
var task = FooAsync();
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
awaiter.OnCompleted(somehow get the current continuation);
// control now returns to the caller; when the task is complete control resumes...
}
// ... here:
result = awaiter.GetResult();
// And now the task builder for the current method is updated with the result.
请注意,这有点复杂,并处理您“等待”已经计算的结果的情况。如果您等待的结果实际上已经为您缓存在内存中,则无需将控制权交给调用者并从中断的地方重新开始。
因此,“await”和“callcc”之间的联系并不像预览版中那么简单,但仍然很清楚,我们本质上是在 awaiter 的“OnCompleted”方法上执行 callcc。如果我们不需要,我们只是不做 callcc 。
更新#2:
作为这个答案
https://stackoverflow.com/a/9826822/88656
Timwi 指出,call/cc 和 await 的语义并不完全相同;“真正的”调用/cc 要求我们“捕获”方法的整个延续,包括其整个调用堆栈,或者等效地,将整个程序重写为延续传递样式。
“等待”功能更像是“合作呼叫/抄送”;继续只捕获“在等待时,当前任务返回方法下一步要做什么?” 如果任务返回方法的调用者在任务完成后要做一些有趣的事情,那么它可以自由地注册它的延续作为任务的延续。