Асинхронность: async
F# выражает асинхронные операции декларативно через async-вычислительные выражения.
async в F# — вычислительное выражение, описывающее асинхронную операцию: оно строит «рецепт» работы, которая выполнится без блокировки потока.
Зачем асинхронность
Операции ввода-вывода (сеть, диск, запрос к БД) долгие. Блокировать поток ожиданием расточительно. Асинхронность позволяет «отпустить» поток на время ожидания и продолжить, когда результат готов — так сервер обслуживает тысячи запросов малым числом потоков.
Блок async { }
Асинхронный код оборачивают в async { ... }. Внутри let! ждёт результат другой асинхронной операции, не блокируя поток.
let fetchData () =
async {
// имитация асинхронной работы
do! Async.Sleep 100
return 42
}
let result =
fetchData ()
|> Async.RunSynchronously
printfn "%d" resultВывод:
42
do! ждёт асинхронную операцию без возврата значения; let! — с возвратом; return завершает асинхронный блок результатом. Async.RunSynchronously запускает «рецепт» и дожидается результата.
Композиция асинхронных шагов
Асинхронные операции естественно соединяются через let! — код читается почти как синхронный, но не блокирует поток.
let step x =
async { return x * 2 }
let pipeline =
async {
let! a = step 10
let! b = step a
return a + b
}
printfn "%d" (Async.RunSynchronously pipeline)Вывод:
60
step 10 даёт 20, step 20 даёт 40, сумма — 60. Каждая let! «ждёт» результат, не занимая поток.
Параллельный запуск
Несколько независимых асинхронных операций можно запустить разом через Async.Parallel.
let tasks = [ step 1; step 2; step 3 ]
let results =
tasks
|> Async.Parallel
|> Async.RunSynchronously
printfn "%A" resultsВывод:
[|2; 4; 6|]
Как работает под капотом
async { } — это вычислительное выражение: компилятор переписывает его в цепочку продолжений (continuations). let! регистрирует «что делать, когда результат готов», а поток тем временем свободен для другой работы. Async в F# совместим с Task из .NET (есть Async.AwaitTask и обратно), поэтому F# легко работает с асинхронными API библиотек C#. В отличие от Task, Async-значение «холодное» — оно не начинает выполняться, пока его явно не запустят.
Частые ошибки
- Забыть
Async.RunSynchronously(илиAsync.Start) —Asyncсам по себе ничего не делает. - Путать
let!(ждёт async с результатом) иdo!(ждёт без результата). - Блокировать поток синхронным ожиданием внутри async вместо
let!.
Итоги
- Асинхронный код пишут в блоке
async { ... }. let!иdo!ожидают асинхронные операции без блокировки потока.Async.RunSynchronouslyзапускает «холодный» async и ждёт результат.Async.Parallelвыполняет независимые операции одновременно; есть мост к .NETTask.