F# 异步计算不需要';t捕捉操作取消异常

F# 异步计算不需要';t捕捉操作取消异常,f#,task-parallel-library,computation-expression,async-workflow,F#,Task Parallel Library,Computation Expression,Async Workflow,我正在尝试向URL发出异步web请求,如果请求时间过长,该URL将返回。我正在使用F#异步工作流和System.Net.Http库来完成这项工作 但是,我无法捕获由async工作流中的System.Net.Http库引发的任务/操作取消异常。相反,在Async.RunSynchronously方法中引发异常,如您在以下堆栈跟踪中所见: > System.OperationCanceledException: The operation was canceled. at > Mi

我正在尝试向URL发出异步web请求,如果请求时间过长,该URL将返回。我正在使用F#异步工作流和System.Net.Http库来完成这项工作

但是,我无法捕获由
async
工作流中的System.Net.Http库引发的任务/操作取消异常。相反,在Async.RunSynchronously方法中引发异常,如您在以下堆栈跟踪中所见:

> System.OperationCanceledException: The operation was canceled.    at
> Microsoft.FSharp.Control.AsyncBuilderImpl.commit[a](Result`1 res)   
> at
> Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously[a](CancellationToken
> token, FSharpAsync`1 computation, FSharpOption`1 timeout)    at
> Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T](FSharpAsync`1
> computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
> at <StartupCode$FSI_0004>.$FSI_0004.main@()
>System.OperationCanceledException:操作已取消。在
>Microsoft.FSharp.Control.AsyncBuilderImpl.commit[a](结果'1 res)
>在
>Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously[a](CancellationToken
>令牌,FSharpAsync`1计算,FSharpOption`1超时)
>Microsoft.FSharp.Control.fsharpsync.RunSynchronously[T](fsharpsync`1
>计算,FSharpOption`1超时,FSharpOption`1取消令牌)
>地址:$FSI_0004.main@()
守则:

#r "System.Net.Http"

open System.Net.Http
open System

let readGoogle () = async {
    try
        let request = new HttpRequestMessage(HttpMethod.Get, "https://google.co.uk")
        let client = new HttpClient()
        client.Timeout <- TimeSpan.FromSeconds(0.01) //intentionally low to always fail in this example
        let! response = client.SendAsync(request, HttpCompletionOption.ResponseContentRead) |> Async.AwaitTask 
        return Some response
    with 
        | ex ->
            //is never called
            printfn "TIMED OUT" 
            return None
}

//exception is raised here
readGoogle ()
    |> Async.RunSynchronously
    |> ignore
#r“System.Net.Http”
开放系统.Net.Http
开放系统
让readGoogle()=async{
尝试
let request=new-HttpRequestMessage(HttpMethod.Get)https://google.co.uk")
let client=new-HttpClient()
client.Timeout Async.waittask
回复
具有
|ex->
//从来没有人打过电话
printfn“超时”
一无所获
}
//这里提出了一个例外
阅读谷歌()
|>异步运行
|>忽略

取消总是与错误不同。在您的情况下,您可以覆盖
AwaitTask
的默认行为,该行为在任务被取消时调用“cancel continuation”,并以不同的方式进行处理:

let readGoogle () = async {
    try
        let request = new HttpRequestMessage(HttpMethod.Get, "https://google.co.uk")
        let client = new HttpClient()
        client.Timeout <- TimeSpan.FromSeconds(0.01) //intentionally low to always fail in this example
        return! ( 
            let t = client.SendAsync(request, HttpCompletionOption.ResponseContentRead)
            Async.FromContinuations(fun (s, e, _) ->
                t.ContinueWith(fun (t: Task<_>) -> 
                    // if task is cancelled treat it as timeout and process on success path
                    if t.IsCanceled then s(None)
                    elif t.IsFaulted then e(t.Exception)
                    else s(Some t.Result)
                )
                |> ignore
            )
        )
    with 
        | ex ->
            //is never called
            printfn "TIMED OUT" 
            return None
}
让readGoogle()=async{
尝试
let request=new-HttpRequestMessage(HttpMethod.Get)https://google.co.uk")
let client=new-HttpClient()
客户端超时
t、 继续(乐趣(任务)->
//若任务被取消,则将其视为超时,并在成功路径上处理
如果t.被取消,则s(无)
elif t.IsFaulted然后是e(t.Exception)
其他s(一些t结果)
)
|>忽略
)
)
具有
|ex->
//从来没有人打过电话
printfn“超时”
一无所获
}

一般来说,异步的异常很有趣。您想使用
Async.Catch
。这种奇怪的行为是由于在主线程上重新抛出异常导致奇数堆栈跟踪而引起的。我已经尝试用Async.Catch样式编写了这篇文章,但问题仍然存在。另一件事是,如果抛出了另一种类型的异常,它将被正确捕获。我想知道异步模块是否处理取消令牌可能是错误的——也许它将异常错误地理解为对模块的取消请求。