Смысл оборачивания Задачи в Async состоит в том, чтобы упростить его компоновку с другими асинхронами или использовать ее с let!
внутри блока async { ... }
. И в последнем случае упакованная Задача не будет запущена, пока не будет запущен включающий ее блок async { ... }
.
Например, давайте посмотрим на следующую функцию:
let printTask str =
async {
printfn "%s" str
} |> Async.StartAsTask
Это мало что дает; его единственная причина существования заключается в том, что вы можете сказать, когда он начал работать, потому что он выведет сообщение на экран. Если вы звоните из F # Interactive:
printTask "Hello"
Вы увидите следующий вывод:
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;}
Таким образом, он напечатал «Hello» и затем вернул завершенное задание Это доказывает, что Задание было начато немедленно.
Но теперь посмотрите на следующий код:
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
}
(Это пример Скотта Влашина "Async Web Downloader" , адаптированный для использования задач вместо внутренних асинхронных).
Здесь блок async { ... }
содержит три задачи, каждая из которых заключена в Async.AwaitTask
. (Обратите внимание, что если вы удалите |> Async.AwaitTask
из любой из этих строк, вы получите ошибку типа). Для каждой задачи после выполнения строки кода она начнется сразу же. Но это важный момент, потому что общее вычисление async { ... }
составляет , а не началось сразу. Так что я могу сделать:
let a = fetchUrlAsync "http://www.google.com"
И единственное, что напечатано в F # Interactive, это val a : Async<unit>
. Я могу ждать столько, сколько захочу, и больше ничего не печатается. Только когда я на самом деле начну a
, он начнет работать:
a |> Async.RunSynchronously
Это печатает started downloading http://www.google.com
сразу, затем после короткой паузы печатает finished downloading http://www.google.com
.
Так что цель Async.AwaitTask
: позволить async { ... }
блокам более легко взаимодействовать с кодом C #, который возвращает Задачи. И если вызов Async.AwaitTask находится внутри блока async { ... }
, то задача на самом деле не запустится, пока не будет запущен включающий Async
, поэтому вы по-прежнему получаете все преимущества «холодного запуска» асинхронных операций.