Плафторма .NET предоставляет множество различного асинхронного функционала для различных целей - для обращения к базам данных, к файлам, для отправки сетевых запросов и так далее. И в F# мы можем использовать весь этот функционал. Рассмотрим на примере статического асинхронного метода File.ReadAllTextAsync(), который считывает текст из файла и возвращает считанный текст.
Для примера определим в папке программы файл "hello.txt" с каким-нибудь текстом, например:
Hello F# Hello Metanit.com
Для считывания этого файла определим следующую программу на F#:
open System.IO // пространство имен, где определен класс File async { let text = File.ReadAllTextAsync("hello.txt") |> Async.AwaitTask |> Async.RunSynchronously printfn "%s" text } |> Async.Start printfn "Program works" printfn "Program ends"
Здесь в функцию File.ReadAllTextAsync()
передается имя файла. Но эта функция возвращает не просто считанный текст,
а объект Task<string> - некоторую задачу, которая выполнится в будущем и которая позволяет получить объект string - считанный текст.
Поэтому ее результат (объект Task) передаем в функцию Async.AwaitTask. Функция Async.AwaitTask будет ожидать выполнения
задачи Task и получит ее результат.
Функция Async.AwaitTask возвращает объект Async<'T> (в данном случае Async<'string>)
где T - тип результата. Чтобы получить данные из Async, передаем результат функции Async.AwaitTask
в функцию
Async.RunSynchronously. Результат функции Async.RunSynchronously и будет представлять собственно считанные из файла данные.
После получения считанных данных выводим их на консоль. Консольный вывод:
Program works Program ends Hello F# Hello Metanit.com
По консольному выводу видно, что блок async со считыванием файлов отработал асинхронно, без блокировки главного потока.
Но с помощью привязки let мы можем сократить код:
async { let! text = File.ReadAllTextAsync("hello.txt") |> Async.AwaitTask printfn "%s" text } |> Async.Start
Для упрощения работы с API, которые возвращают объекты Task в .NET, также можно использовать блок выражений task { ... }. Внутрь фигурных скобок помещается набор выражений, которые выполняют задачи Task. Так, перепишем предыдущий пример с использованием этого блока:
open System.IO task { let! text = File.ReadAllTextAsync("hello.txt") printfn "%s" text } printfn "Program works" printfn "Program ends"
Выражения в блоке task запускаются сразу после выполнения этого кода и выполняются в текущем потоке до тех пор, пока не встретися
первая асинхронная операция (например, метод File.ReadAllTextAsync
). Чтобы получить результат асинхронного метода применяется привязка let!
let! text = File.ReadAllTextAsync("hello.txt")