F# 异步相当于 Promise.race?

问题描述 投票:0回答:3

在 JavaScript 中,有一个名为

Promise.race
的函数,它接受 Promise 列表并返回一个新 Promise,当 any 的输入 Promise 完成时,该新 Promise 也完成。

请参阅:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race

F# 有

Async.Parallel
,当输入异步的 all 完成时完成,但它似乎没有 any 的等价物(例如
Async.Race
)。

如何在 F# 中编写此代码?

asynchronous f#
3个回答
3
投票

可以使用任务。

类似这样的:

let race (xs : Async<'t> seq) : Async<'t> =
  async {
    let! ct = Async.CancellationToken

    let! t =
      xs
      |> Seq.map (fun x -> Async.StartAsTask(x, cancellationToken = ct))
      |> Task.WhenAny
      |> Async.AwaitTask

    return! Async.AwaitTask t
  }

1
投票

使用

Async.Choice
中的
FSharp.Control

let race xs = 
  async {
    let! first = 
      xs
      |> Seq.map (fun task -> async {
        let! x = task
        return Some x
      })
      |> Async.Choice

    return Option.get first
  }

0
投票

另一种选择是(ab)使用异常机制来获得提前返回。

[<RequireQualifiedAccess>]
module Async =

  open System

  /// An exception that carries a success value
  type internal ShortCircuit<'t>(value : 't) =
    inherit Exception()
    member this.Value = value

  let race (a : Async<'a>) (b : Async<'b>) : Async<Choice<'a * Async<'b>, Async<'a> * 'b>> =
    async {
      let! a = Async.StartChild a
      let! b = Async.StartChild b

      let x = 
        async {
          let! a = a

          let choice : Choice<'a * Async<'b>, Async<'a> * 'b> =
            Choice1Of2(a, b)

          raise (ShortCircuit choice)
        }
      
      let y = 
        async {
          let! b = b

          let choice : Choice<'a * Async<'b>, Async<'a> * 'b> =
            Choice2Of2(a, b)

          raise (ShortCircuit choice)
        }
      
      try 
        do! 
          Async.Parallel([| x; y |])
          |> Async.Ignore

        return failwith "Unreachable"
      with :? ShortCircuit<Choice<'a * Async<'b>, Async<'a> * 'b>> as sc -> 
        return sc.Value
    }

用途:

let foo = 
  async {
    printfn "foo started"

    do! Async.Sleep 1000

    return "foo"
  }

let bar = 
  async {
    printfn "bar started"

    do! Async.Sleep 5000
    
    return "bar"
  }

async {
  printfn "Racing..."

  match! Async.race foo bar with
  | Choice1Of2 (a, b) ->
    printfn $"a = %s{a}"
    let! b = b
    printfn $"b = %s{b}"
  | Choice2Of2 (a, b) ->
    printfn $"b = %s{b}"
    let! a = a
    printfn $"a = %s{a}"
}
|> Async.RunSynchronously
© www.soinside.com 2019 - 2024. All rights reserved.