Coding Edgar
Coding Edgar

Reputation: 1475

When to use Async.RunSynchronously, Async.map or let

Consider these 3 versions

async {
  return! retrieved =
             client
             |> get tagIdOfQid None
             |> Async.Catch
             |> Async.map (function
                  | Choice1Of2 doc ->
                      doc
                      |> mapDoc
                      |> function
                      | IdOfQ d -> d
                      | _ -> defaultTagIdOfQ
                  | _ -> defaultTagIdOfQ)
}
async {
  return retrieved =
             client
             |> get tagIdOfQid None
             |> Async.Catch
             |> Async.RunSynchronously 
             |> function
                  | Choice1Of2 doc ->
                      doc
                      |> mapDoc
                      |> function
                      | IdOfQ d -> d
                      | _ -> defaultTagIdOfQ
                  | _ -> defaultTagIdOfQ)
}
async {
  let! retrieved =
             client
             |> get tagIdOfQid None
             |> Async.Catch
   
   return! retrieved
           |> function
           | Choice1Of2 doc ->
              doc
              |> mapDoc
              |> function
              | IdOfQ d -> d
              | _ -> defaultTagIdOfQ
           | _ -> defaultTagIdOfQ
}

I want to pipe the result of the get to another computation, I can use Async.map, Async.RunSynchronously or add multiple let! bindings.

I see them all as valid alternatives for doing the same, without any differences, but maybe I'm wrong and they are doing slightly different things.

Is there any tradeoff between Async.map, Async.RunSynchronously and let!?

Upvotes: 0

Views: 237

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243126

The key difference is that if you use Async.RunSynchronously, even if this is inside another async workflow, this will block a thread when you run it. Using Async.map will not block and multiple let! is the same in this respect.

So, I would avoid using Async.RunSynchronously. As to what else to do, this is a style preference.

Using async { return! e } does not make sense, because it means exactly the same as just e, so you can simplify the first approach. I also replaced the confusing (to me at least) inner pipe with ordinary match:

client
|> get tagIdOfQid None
|> Async.Catch
|> Async.map (function
    | Choice1Of2 doc ->
        match mapDoc doc with 
        | IdOfQ d -> d
        | _ -> defaultTagIdOfQ
    | _ -> defaultTagIdOfQ)

In the second approach, I would also remove some of the pipelines, because I find them hard to follow. If you are inside async you can also use ordinary try ... with instead of Async.Catch:

async {
  try 
    let! doc = get tagIdOfQid None client
    match mapDoc doc with 
    | IdOfQ d -> return d
    | _ -> return defaultTagIdOfQ
   with _ -> return defaultTagIdOfQ 
}

If I was writing this, I would go with the latter - mainly because it is very easy to read & understand what is going on.

Upvotes: 3

Related Questions