Task.ContinueWith 的 F# 异步等价物

标签 f# fody continuewith async-workflow

我一直在实现 [<Trace>]我们一些较大的 .NET 解决方案的属性,允许将可配置的分析轻松添加到任何被认为重要的功能/方法中。我正在使用 Fody 和 MethodBoundaryAspect拦截每个函数的进入和退出并记录指标。这适用于同步函数和返回 Task 的方法。 Task.ContinueWith 有一个可行的解决方案,但对于 F# 异步返回函数,OnExit MethodBoundaryAspect 在 Async 返回后立即运行(而不是在实际执行 Async 时)。

为了捕获 F# 异步返回函数的正确指标,我试图想出一个等效的解决方案来使用 Task.ContinueWith ,但我能想到的最接近的事情是创建一个新的 Async 绑定(bind)第一个,运行度量捕获函数,然后返回原始结果。由于我拦截的 F# Async 返回值仅显示为 obj,这使情况变得更加复杂。 ,之后我必须反射(reflection)地做所有事情,因为没有非通用版本的 Async就像有 Task我可以在不知道确切返回类型的情况下使用。

到目前为止,我最好的解决方案大致如下所示:

open System
open System.Diagnostics
open FSharp.Reflection
open MethodBoundaryAspect.Fody.Attributes

[<AllowNullLiteral>]
[<AttributeUsage(AttributeTargets.Method ||| AttributeTargets.Property, AllowMultiple = false)>]
type TraceAttribute () =
    inherit OnMethodBoundaryAspect()

    let traceEvent (args: MethodExecutionArgs) (timestamp: int64) =
        // Capture metrics here
        ()

    override __.OnEntry (args) =
        Stopwatch.GetTimestamp() |> traceEvent args

    override __.OnExit (args) =
        let exit () = Stopwatch.GetTimestamp() |> traceEvent args
        match args.ReturnValue with
        | :? System.Threading.Tasks.Task as task ->
            task.ContinueWith(fun _ -> exit()) |> ignore             
        | other -> // Here's where I could use some help
            let clrType = other.GetType()
            if clrType.IsGenericType && clrType.GetGenericTypeDefinition() = typedefof<Async<_>> then
                // If the return type is an F# Async, replace it with a new Async that calls exit after the original return value is computed
                let returnType = clrType.GetGenericArguments().[0]
                let functionType = FSharpType.MakeFunctionType(returnType, typedefof<Async<_>>.MakeGenericType([| returnType |]))
                let f = FSharpValue.MakeFunction(functionType, (fun _ -> exit(); other))
                let result = typeof<AsyncBuilder>.GetMethod("Bind").MakeGenericMethod([|returnType; returnType|]).Invoke(async, [|other; f|]) 
                args.ReturnValue <- result
            else
                exit()

不幸的是,这个解决方案不仅非常困惑,而且我相信异步计算的反射构造会增加大量的开销,尤其是当我试图跟踪在循环中调用的函数或有深层嵌套的异步调用。有没有更好的方法来实现在实际评估异步计算后立即运行给定函数的相同结果?

最佳答案

这样的东西可能是你需要的:

let traceAsync (a:Async<_>) = async {
    trace() // trace start of async
    let! r = a
    trace() // trace end of async
    return r
}

考虑一下,当一个函数返回一个异步并不意味着异步已经开始。异步更像是一个函数,它可以被调用多次或根本不被调用。这意味着您还需要在 OnEntry 方法中检查返回值是否为 Async。

关于Task.ContinueWith 的 F# 异步等价物,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55288319/

相关文章:

f# - 如何使用 Fable (F#) 进行 base64 编码?

f# - Fable-Elmish 中的渲染组件

c# - 如何仅将方法参数中对象的某些属性传递给 Fody/MethodTimer Interceptor

xamarin.ios - 如何让 Fody/PropertyChanged 在 Xamarin Studio 的 iOS 项目中工作?

C# Chained ContinueWith 不等待上一个任务完成

c# - 将任务转换为任务<T>(包装任务,返回类型为 T)

c# - ContinueWith 没有捕获到异常?

F# 数据提要抽象

haskell - 在 Haskell 中实现 FSharp 的 Int32.TryParse

msbuild - NuGet 包还原不获取构建目标程序集(工具)