Последовательное выполнение асинхронных операций в 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
Другие вопросы по тегам