F# Is there a Async.Sequential to match Async.Parallel?

Viewed 862

F# has Async.Parallel with type signature seq<Async<'a>> -> Async<'a list>, which will take a seq of Async types, prep them to run in parallel, and output a single Async.

I was wondering if there is a similar Async.Sequential which has the same type signature, but runs each item in sequence one after the other? I feel like I must be missing something obvious?

I'd like to do something like the following:

async {
    let! results =
        myItems
        |> List.map myAsynchronousOperation
        |> Async.Sequential

    ... do something with results
}
4 Answers

I don't think there's one built in, but should be trivial to write your own:

let sequential s = async {
    let results = ResizeArray()
    for a in s do
        let! result = a
        results.Add(result)
    return List.ofSeq results
}

Evaluating the items in a seq<Async<'a>> in-order and returning a list is effectively just a foldBack on the sequence where you want to evaluate the async before simply cons-ing it onto a list. While kvb's solution certainly works, you could also do it with a Seq.foldBack like so:

module Async =
    let sequential s = 
        Seq.foldBack (fun cur acc -> async.Bind(cur, fun head -> 
            async.Bind(acc, fun tail -> async.Return(head :: tail)))) s (async.Return [])

If one is willing to use the AsyncSeq library, then the following does what's needed in the given example:

async {
    let! results =
        myItems
        |> AsyncSeq.ofSeq
        |> AsyncSeq.mapAsync myAsynchronousOperation
        |> AsyncSeq.toListAsync

    ... do something with results
}

Probably overkill if you're not already using AsyncSeq for other things though.

Related