You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在F#中等待多个Async任务,取首个结果并取消其余?

实现F#异步任务的"先完成即返回"并取消其余任务

一、仅使用F#标准库实现

核心是通过取消令牌(CancellationToken)实现:当第一个任务完成时,触发取消信号终止其余未完成的任务,同时利用Async.Choice等待首个完成的任务。

实现代码

module Async =
    /// 等待第一个完成的异步任务,取消其余任务
    let whenAny (asyncs: Async<'T> list) =
        async {
            use cancellationSource = new System.Threading.CancellationTokenSource()
            // 包装每个任务:完成后触发全局取消
            let wrappedAsyncs =
                asyncs
                |> List.map (fun asyncTask ->
                    async {
                        let! result = asyncTask |> Async.WithCancellationToken cancellationSource.Token
                        cancellationSource.Cancel() // 第一个完成的任务取消其余任务
                        return result
                    })
            // 等待第一个成功完成的任务
            return! Async.Choice wrappedAsyncs
        }

使用示例

let fetchNumber (source : string) =
  async {
    let r = Random()
    let delay = r.Next(3_000)
    do! Async.Sleep(delay)
    printfn $"[{source}] 完成请求,耗时 {delay}ms"
    return r.Next(6)
  }

// 调用自定义的whenAny
async {
    let! firstResult =
        Async.whenAny
            [
                fetchNumber "http://a.com"
                fetchNumber "http://b.com"
                fetchNumber "http://c.com"
            ]
    printfn $"首个返回结果: {firstResult}"
} |> Async.RunSynchronously

二、借助常用库实现

可以利用.NET的Task.WhenAny结合F#异步任务的转换来快速实现,依赖F#核心库的异步转Task能力即可:

实现代码

open System.Threading.Tasks

module Async =
    let whenAny (asyncs: Async<'T> list) =
        async {
            // 将所有异步任务转为Task
            let tasks = asyncs |> List.map (fun a -> Async.StartAsTask(a, cancellationToken = System.Threading.CancellationToken.None))
            // 等待第一个完成的Task
            let! completedTask = Task.WhenAny(tasks) |> Async.AwaitTask
            // 取消剩余未完成的任务
            tasks
            |> List.filter (fun t -> t <> completedTask && not t.IsCompleted)
            |> List.iter (fun t -> t.Cancel())
            return completedTask.Result
        }

说明

Async.StartAsTask会将F#异步任务包装为可取消的.NET Task,Task.WhenAny原生支持等待首个完成的任务,随后我们手动取消剩余未完成的Task即可。

内容的提问来源于stack exchange,提问作者sdgfsdh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 22:43:10