2

I have to make several calls to an API over the network.

let RestCall1 args = async{ Thread.Sleep (1000); return args}
let RestCall2 args = async{Thread.Sleep (1000); return args} 

I know that an async{...} context has a performance hit, so I'm trying to minimize the number of contexts I create. Plus it's awkward to pipeline.

let nested4 args = async{ let! x = RestCall1 args; return args }
let nested7 args = async{ let! x = RestCall2 args; return args }

let outerAsync (args : string) =
    async {
        let args1 = args
                    |> nested1
                    |> nested2
                    |> nested3
        let! args2 = nested4 args1
        let args3 = args2
                    |> nested5
                    |> nested6
        return nested7 args3          
    }

Is there a way of doing it (like the sample below) without blocking the thread with RunSynch?

let nested4 args = RestCall1 args |> Async.RunSynchronously
let nested7 args = RestCall2 args |> Async.RunSynchronously

let outerAsync (args : string) =
    async {
        return
            args
            |> nested1
            |> nested2
            |> nested3
            |> nested4
            |> nested5
            |> nested6
            |> nested7 
    }
2
  • I'm realizing there is no free lunch. The thread either blocks or switches to an async context. Nested 4 and 7 do stuff with the API call, but I can refactor to avoid the extra async block there. Still looking for a more graceful way to pipe tho. Commented Nov 12, 2020 at 22:37
  • 1
    For the last part you could use a library which gives you Async.map and Async.bind (e.g. fsprojects.github.io/FSharpx.Async/reference/…). This will be purely to clean up the code though. It won't affect how it runs. Commented Nov 13, 2020 at 11:02

1 Answer 1

2

You are correct in wanting to avoid Async.Runsynchronously, which will block (see "Does Async.RunSynchronously block?").

As TheQuickBrownFox suggests, you want a library that supplies Async.map and Async.bind. Here I have used FsToolkit.ErrorHandling:

  open System.Threading
  open FsToolkit.ErrorHandling

  let restCall1 args = async{ Thread.Sleep (1000); return args }
  let restCall2 args = async{ Thread.Sleep (1000); return args }

  let nested1 args = args
  let nested2 args = args
  let nested3 args = args
  let nested4 args = restCall1 args
  let nested5 args = args
  let nested6 args = args
  let nested7 args = restCall2 args

  let outerAsync (args : string) : Async<string> =
    args
    |> nested1
    |> nested2
    |> nested3
    |> nested4
    |> Async.map nested5
    |> Async.map nested6
    |> Async.bind nested7

Async.map takes nested5 and changes its signature from (string -> string) to (Async<string> -> Async<string>). It is called a functor.

Async.bind takes nested7 and changes its signature from (string -> Async<string>) to (Async<string> -> Async<string>).

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

Comments

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.