在 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# 中编写此代码?
您可以使用任务。
类似这样的:
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
}
使用
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
}
另一种选择是(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