在内部lambda中调用异步方法? "此构造只能在计算表达式中使用"

时间:2016-07-18 15:39:54

标签: f#

我有以下代码

let rec consume() : Async<unit> = async {
    .....
    listA 
    |> Seq.iter(fun i ->
        .....
        let listB : seq<...> option = 
            let c = getListB a b
            match c with 
            | Some d -> Seq.filter(....) |> Some
            | None -> None 
        match listB with .....
    ....

现在,函数getListB已转换为返回async<Seq<B>>而不是Seq<B>。所以代码转换为以下内容。但是,getListB阻止了执行。如何重写非阻塞?只需将行转换为let! c = getListB a b即可,因为代码位于内部lambda中?错误消息是&#34;此构造只能在计算表达式中使用&#34;。

let rec consume() : Async<unit> = async {
    .....
    listA 
    |> Seq.iter(fun i ->
        .....
        let listB : seq<...> option = 
            let c = getListB a b |> Async.RunSynchronously
            match c with 
            | Some d -> Seq.filter(....) |> Some
            | None -> None 

2 个答案:

答案 0 :(得分:2)

答案取决于您是要按顺序还是并行运行序列的每个元素。

在这两种情况下,首先使用Seq.map而不是Seq.iter,然后您可以在lambda中放置另一个async块,以使map的结果为{ {1}}。

<强>顺序

为此,您需要在额外的seq<Async<'a>>模块中定义一些额外的功能。

Async

您可能在其他地方看到module Async = let map f x = async{ let! x = x return f x } let lift2 f x1 x2 = async{ let! x1 = x1 let! x2 = x2 return f x1 x2 } let return' x = async { return x } let mapM mFunc sequ = let consF x ys = lift2 (fun h t -> h::t) (mFunc x) ys Seq.foldBack(consF) sequ (return' []) |> map (Seq.ofList) let sequence sequ = mapM id sequ 被称为mapM,它们基本上只是同一概念的不同名称。

traverse函数只是sequence的特例,其中提供的绑定函数只是标识(mapM)函数。它的类型为id,即它会将seq<Async<'a>> -> Async<seq<'a>>Async内翻到外面。

然后,您只需将Seq的结果传输到Seq.map函数,该函数会为您提供sequence值。

您的示例代码不完整所以我编写了一些示例代码来使用它:

async
let sleep = Async.Sleep 100
let sleeps = Seq.init 15 (fun _ -> sleep)
let sequencedSleeps = Async.sequence sleeps
Async.RunSynchronously sequencedSleeps

<强>并行

要并行执行序列的每个元素,而不是按顺序执行:

Real: 00:00:01.632, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val it : seq<unit> =
  [null; null; null; null; null; null; null; null; null; null; null; null;
   null; null; null]

示例测试代码:

let pSequence sequ = Async.Parallel sequ |> Async.map (Seq.ofArray)
let pSleeps = pSequence sleeps;;
Async.RunSynchronously pSleeps;;

请注意执行时间取决于所选择的方法。

对于你回来Real: 00:00:00.104, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 val it : seq<unit> = seq [null; null; null; null; ...] 并因此想要忽略结果的情况,定义一些额外的辅助函数会很有用,例如:

seq<unit>

这可以让你返回一个let sequenceIgnore sequ = sequ |> Async.sequence |> Async.map (ignore) let pSequenceIgnore sequ = sequ |> pSequence |> Async.map (ignore) 而不是多余的序列。

答案 1 :(得分:2)

我认为您所描述的问题归结为如何将seq<Async>转换为Async<seq>。 Scott Wlaschin在this post中对此进行了全面描述。

这是一个穷人在他的帖子中描述的概念的实现,它们更加强大和通用。一般的想法是我们想要延迟序列的创建,直到我们拥有Async<_>

实例承诺的值为止
let traverseSequence ( seqAsync : seq<Async<'a>>) = 

    let promiseOfAnEmptySequence = async  { return Seq.empty }

    let delayedCalculation (asyncHead : Async<'a>) (asyncTail : Async<seq<'a>>) = 
        async {
        let! calculatedHead = asyncHead
        return!
            async {
                let! calculatedTail = asyncTail
                return calculatedHead |> Seq.singleton |> Seq.append(calculatedTail)
            }
        }

    Seq.foldBack delayedCalculation seqAsync promiseOfAnEmptySequence