8

I have two asynchronous operations, so I have these functions:

// Returs Async<Person>
let GetPerson =
  ...

// Returs Async<Address>
let GetAddress =
  ...

What is the idiomatic way to execute them in parallel and get their results?

My starting point is this approach.

let MyFunc = async {
    let! person = GetPerson()
    let! address = GetAddress()
    ...
  }

This works, but this runs the two operations sequentially.

I also tried this (sort of based on my C# experience).

let MyFunc = async {
    let personA = GetPerson()
    let addressA = GetAddress()
    let! person = personA
    let! address = addressA
    ...
  }

But it doesn't work, it also runs the two operations sequentially.

What most of the documentation says is to use Async.Parallel with a sequence, but the problem is that the result type of the two operations are different, so I cannot put them in a sequence.

let MyFunc = async {
    let personA = GetPerson()
    let addressA = GetAddress()

    [personA; addressA]
    |> Async.Parallel
    ...
  }

This gives a compilation error, because the two values have different types. (And also, with this syntax, how could I get the actual results?)

What is the idiomatic way to do this?

1 Answer 1

10

The idiomatic approach is to start both computations using Async.StartAsChild and then wait for their completion using a second let!:

let MyFunc = async {
    let! personWork = GetPerson() |> Async.StartChild
    let! addressWork = GetAddress() |> Async.StartChild
    let! person = personWork
    let! address = addressWork
    // (...)
  }

Just calling GetPerson does not actually start the work - unlike in C# where tasks are created started, F# workflows are just descriptions of the work to be done, so they need to be started explicitly. The Async.StartChild operation gives you a workflow that starts the work and returns another workflow that can be used for wait for its completion.

Sign up to request clarification or add additional context in comments.

6 Comments

IF GetPerson and GetAddress are also async primitives, they by convention should they also be prefixed with async. i.e. asyncGetPerson and asyncGetAddress ?
That is certainly the case for F# library functions (AsyncGetResponse vs. GetResponse). I'd follow this pattern if I had both synchronous and asynchronous versions, but probably not when writing code that only has asynchronous version.
@TomasPetricek I noticed that for Async.Merge on your joinads blog you used Async.Parallel instead of this 'idiomatic' approach. I'm curious about why did you use such approach, is it more efficient for a specific scenario?
@Gustavo: Async.Parallel only works for async jobs that return the same type, but this S.O. question is about differing types
@knocte I know, but if you have a look at the code I was referring you'll see it uses boxing to allow the types to differ.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.