简体   繁体   English

如何使用回调从C#方法创建F#异步?

[英]How can I create an F# async from a C# method with a callback?

Suppose I have some C# code that takes a callback: 假设我有一些C#代码需要回调:

void DoSomething(Action<string> callback);

Now, I want to use this in F#, but wrap it in an async . 现在,我想在F#中使用它,但将其包装在async How would I go about this? 我该怎么做?

// Not real code
let doSomething = async {
  let mutable result = null
  new Action(fun x -> result <- x) |> Tasks.DoSomething
  // Wait for result to be assigned
  return result
}

For example, suppose DoSomething looks like this: 例如,假设DoSomething看起来像这样:

module Tasks

  let DoSomething callback = 
    callback "Hello"
    ()

Then the output of the following should be "Hello" : 然后以下输出应为"Hello"

let wrappedDoSomething = async {
   // Call DoSomething somehow
}

[<EntryPoint>]
let main argv =
  async {
    let! resultOfDoSomething = wrappedDoSomething
    Console.WriteLine resultOfDoSomething
    return ()
  } |> Async.RunSynchronously
  0

The function Async.FromContinuations is, so to say, the "lowest level" of Async. Async.FromContinuations函数可以说是Async的“最低级别”。 All other async combinators can be expressed in terms of it. 所有其他异步组合器都可以用它来表示。

It is the lowest level in the sense that it directly encodes the very nature of async computations - the knowledge of what to do in the three possible cases: (1) a successful completion of the previous computation step, (2) a crash of the previous computation step, and (3) cancellation from outside. 它是最低级别,它直接编码异步计算的本质 - 在三种可能情况下做什么的知识:(1)成功完成前一个计算步骤,(2)崩溃先前的计算步骤,以及(3)从外部取消。 These possible cases are expressed as the three function-typed arguments of the function that you pass to Async.FromContinuations . 这些可能的情况表示为传递给Async.FromContinuations的函数的三个函数类型参数。 For example: 例如:

let returnFive = 
    Async.FromContinuations( fun (succ, err, cancl) ->
        succ 5
    )

async {
    let! res = returnFive
    printfn "%A" res  // Prints "5"
} 
|> Async.RunSynchronously

Here, my function fun (succ, err, cancl) -> succ 5 has decided that it has completed successfully, and calls the succ continuation to pass its computation result to the next step. 在这里,我的函数fun (succ, err, cancl) -> succ 5已经确定它已经成功完成,并调用succ continuation将其计算结果传递给下一步。

In your case, the function DoSomething expresses only one of the three cases - ie "what to do on successful completion". 在您的情况下,函数DoSomething只表达三种情况中的一种 - 即“成功完成后该怎么做”。 Once you're inside the callback, it means that whatever DoSomething was doing, has completed successfully. 一旦你进入回调,这意味着无论DoSomething做了什么,都已成功完成。 That's when you need to call the succ continuation: 那时你需要调用succ continuation:

let doSometingAsync = 
    Async.FromContinuations( fun (succ, err, cancl) -> 
        Tasks.DoSomething( fun res -> succ res ) 
    )

Of course, you can avoid a nested lambda-expression fun res -> succ res by passing succ directly into DoSomething as callback. 当然,你可以通过将succ直接传递给DoSomething作为回调来避免嵌套的lambda-expression fun res -> succ res Unfortunately, you'll have to explicitly specify which type of Action to use for wrapping it, which negates the advantage: 不幸的是,您必须明确指定用于包装它的Action类型,否则这会取消优势:

let doSometingAsync = 
    Async.FromContinuations( fun (succ, err, cancl) -> 
        Tasks.DoSomething( System.Action<string> succ ) 
    )

As an aside, note that this immediately uncovered a hole in the DoSomething 's API: it ignores the error case. 顺便说一句,请注意,这立即在DoSomething的API中发现了一个漏洞:它忽略了错误情况。 What happens if DoSomething fails to do whatever it was meant to do? 如果DoSomething无法做任何意图,会发生什么? There is no way you'd know about it, and the whole async workflow will just hang. 您无法了解它,整个异步工作流程将会挂起。 Or, even worse: the process will exit immediately (depending on how the crash happens). 或者,更糟糕的是:进程将立即退出(取决于崩溃的发生方式)。

If you have any control over DoSomething , I suggest you address this issue. 如果您对DoSomething有任何控制权,我建议您解决此问题。

You can try something like: 您可以尝试以下方式:

let doSomething callback = async {
    Tasks.DoSomething(callback)
}

If your goal is to define the callback in the method you could do something like: 如果您的目标是在方法中定义回调,则可以执行以下操作:

let doSomething () = async {
    let callback = new Action<string>(fun result -> printfn "%A" result )
    Tasks.DoSomething(callback)
}

If your goal is to have the result of the async method be used in the DoSomething callback you could do something like: 如果您的目标是在DoSomething回调中使用async方法的结果,您可以执行以下操作:

let doSomething =

         Async.StartWithContinuations(
         async {
            return result
            },
         (fun result -> Tasks.DoSomething(result)),
         (fun _ -> printfn "Deal with exception."),
         (fun _ -> printfn "Deal with cancellation."))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM