Последовательное выполнение асинхронных операций в F#
Есть ли какой-нибудь примитив в языке для составления async1, а затем async2, сродни тому, что делает параллель для планирования параллельного выполнения?
Чтобы уточнить, у меня есть 2 асинхронных вычислений
let toto1 = Async.Sleep(1000)
let toto2 = Async.Sleep(1000)
Я хотел бы создать новое асинхронное вычисление, сделанное из последовательной композиции toto1 и toto2
let toto = Async.Sequential [|toto1; toto2|]
при запуске toto запускается toto1, затем toto2 и заканчивается через 2000 единиц времени
2 ответа
async.Bind
операция является основным примитивом, который асинхронные рабочие процессы обеспечивают для последовательной композиции - в async
блочный синтаксис, соответствующий let!
, Вы можете использовать это для выражения последовательной композиции двух вычислений (как продемонстрировал Даниэль).
Тем не менее, если у вас есть операция <|>
что Даниил определил, чем это не достаточно выразительно, чтобы реализовать async.Bind
потому что, когда вы сочиняете вещи последовательно, используя async.Bind
второе вычисление может зависеть от результата первого. <e2>
может использовать v1
:
async.Bind(<e1>, fun v1 -> <e2>)
Если бы вы писали <e1> <|> <e2>
тогда две операции должны быть независимыми. Это причина, почему библиотеки основаны на Bind
- потому что это более выразительная форма последовательной композиции, чем та, которую вы получили бы, если бы следовали структуре Async.Parallel
,
Если вы хотите что-то, что ведет себя как Async.Parallel
и принимает массив, то самый простой вариант заключается в реализации этого императивно с использованием let!
в цикле (но вы также можете использовать рекурсию и списки):
let Sequential (ops:Async<'T>[]) = async {
let res = Array.zeroCreate ops.Length
for i in 0 .. ops.Length - 1 do
let! value = ops.[i]
res.[i] <- value
return res }
Я не уверен, что вы подразумеваете под "примитивом". Async.Parallel
это функция. Вот несколько способов запустить две асинхронные программы:
В параллели:
Async.Parallel([|async1; async2|])
или же
async {
let! child = Async.StartChild async2
let! result1 = child
let! result2 = async1
return [|result1; result2|]
}
Последовательная:
async {
let! result1 = async1
let! result2 = async2
return [|result1; result2|]
}
Вы можете вернуть кортежи в последних двух. Я сохранил типы возврата такими же, как и первый.
я бы сказал let!
а также do!
в async { }
Блок настолько близок, насколько вы можете использовать примитив для этого.
РЕДАКТИРОВАТЬ
Если вам нужен весь этот неприятный синтаксис, вы можете определить комбинатор:
let (<|>) async1 async2 =
async {
let! r1 = async1
let! r2 = async2
return r1, r2
}
а затем сделать:
async1 <|> async2 |> Async.RunSynchronously