Async.AwaitTask如何在f#中工作?

use*_*180 9 f#

我理解f#和c#异步模型之间的主要区别在于f#中,除非你调用类似Async.RunSynchronously的东西,否则异步执行不会开始.在c#中,当方法返回任务时,通常(并非总是)立即在后台线程中启动执行.

Async.AwaitTask文档说"返回一个等待给定任务完成并返回其结果的异步计算."

这是否意味着,当您调用返回任务的ac#方法时,执行已经在后台启动了?如果是这样,那么将它包装在Async类型中又有什么意义呢?

rmu*_*unn 15

在Async中包装Task的目的是更容易地将其与其他asyncs组合,或者let!async { ... }块内部使用它.在后一种情况下,包装的任务将在其封闭async { ... }块启动之前不会启动.

例如,让我们看看以下函数:

let printTask str =
  async {
    printfn "%s" str
  } |> Async.StartAsTask
Run Code Online (Sandbox Code Playgroud)

这没什么用; 它存在的唯一原因是你可以告诉它什么时候开始运行,因为它会在屏幕上打印一条消息.如果你从F#Interactive调用它:

printTask "Hello"
Run Code Online (Sandbox Code Playgroud)

您将看到以下输出:

Hello
val it : Threading.Tasks.Task<unit> =
  System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit]
    {AsyncState = null;
     CreationOptions = None;
     Exception = null;
     Id = 4;
     IsCanceled = false;
     IsCompleted = true;
     IsCompletedSuccessfully = true;
     IsFaulted = false;
     Status = RanToCompletion;}
Run Code Online (Sandbox Code Playgroud)

所以它打印"Hello"然后返回完成的Task.这证明该任务立即启动.

但现在看下面的代码:

open System.Net
open System
open System.IO

let printTask str =
  async {
    printfn "%s" str
  } |> Async.StartAsTask

let fetchUrlAsync url =
  async {
    let req = WebRequest.Create(Uri(url))
    do! printTask ("started downloading " + url) |> Async.AwaitTask
    use! resp = req.GetResponseAsync() |> Async.AwaitTask
    use stream = resp.GetResponseStream()
    use reader = new IO.StreamReader(stream)
    let html = reader.ReadToEnd()
    do! printTask ("finished downloading " + url) |> Async.AwaitTask
  }
Run Code Online (Sandbox Code Playgroud)

(这是Scott Wlaschin的"Async Web Downloader"示例,适用于在内部使用Tasks而不是Asyncs).

这里,该async { ... }块包含三个任务,所有任务都包含在内Async.AwaitTask.(请注意,如果|> Async.AwaitTask从这些行中删除任何一行,则会出现类型错误).对于每个任务,一旦其代码行执行,它将立即开始.但这是一个重要的观点,因为整体async { ... }计算并没有立即开始.所以我可以这样做:

let a = fetchUrlAsync "http://www.google.com"
Run Code Online (Sandbox Code Playgroud)

而F#Interactive中唯一印刷的是val a : Async<unit>.我可以等到我想要的时间,也没有别的东西打印出来.只有我真正开始 a它才会开始运行:

a |> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)

这会started downloading http://www.google.com立即打印,然后在短暂停顿后打印finished downloading http://www.google.com.

这样做的目的是Async.AwaitTask:允许async { ... }块更容易地与返回Tasks的C#代码互操作.而如果Async.AwaitTask调用内部async { ... }的块,然后将任务才会真正开始直至关闭Async开始,所以你仍然可以得到的"冷启动"异步操作的所有优点.