последовательное выполнение цепочек асинхронных операций в F # - PullRequest
2 голосов
/ 08 марта 2012

Есть ли какой-либо примитив в языке для составления 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 ]

4 голосов
/ 09 марта 2012

Операция 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 }
2 голосов
/ 08 марта 2012

Я не уверен, что вы подразумеваете под "примитивом". 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 { } настолько близки к использованию примитива для этого.

EDIT

Если вам нужен весь этот неприятный синтаксис, вы можете определить комбинатор:

let (<|>) async1 async2 = 
  async {
    let! r1 = async1
    let! r2 = async2
    return r1, r2
  }

и затем сделайте:

async1 <|> async2 |> Async.RunSynchronously
...