Как я могу создать F # асинхронный из метода C # с обратным вызовом? - PullRequest
0 голосов
/ 10 июня 2018

Предположим, у меня есть некоторый код C #, который принимает обратный вызов:

void DoSomething(Action<string> callback);

Теперь я хочу использовать это в F #, но обернуть его в async.Как бы я пошел по этому поводу?

// 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
}

Например, предположим, что DoSomething выглядит следующим образом:

module Tasks

  let DoSomething callback = 
    callback "Hello"
    ()

Тогда на выходе должно быть "Hello":

let wrappedDoSomething = async {
   // Call DoSomething somehow
}

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

Ответы [ 2 ]

0 голосов
/ 10 июня 2018

Функция Async.FromContinuations является, так сказать, «самым низким уровнем» Async.Все другие асинхронные комбинаторы могут быть выражены в терминах этого.

Это самый низкий уровень в том смысле, что он непосредственно кодирует саму природу асинхронных вычислений - знание того, что делать в трех возможных случаях: (1) успешное завершение предыдущего шага вычисления, (2) сбой предыдущего шага вычисления и (3) отмена извне.Эти возможные случаи выражаются в виде трех аргументов типа функции функции, которую вы передаете Async.FromContinuations.Например:

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

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

Здесь моя функция fun (succ, err, cancl) -> succ 5 решила, что она успешно выполнена, и вызывает продолжение succ, чтобы передать результат вычисления на следующий шаг.

В вашем случае функция DoSomething выражает только один из трех случаев - то есть «что делать при успешном завершении».Как только вы попадаете в обратный вызов, это означает, что все, что делал DoSomething, завершилось успешно.Вот тогда вам нужно вызвать продолжение succ:

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

Конечно, вы можете избежать вложенного лямбда-выражения fun res -> succ res, передав succ непосредственно в DoSomething в качестве обратного вызова.К сожалению, вам придется явно указать, какой тип Action использовать для его обертывания, что сводит на нет преимущество:

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

Обратите внимание, что это сразу же обнаружило дыру вAPI DoSomething: он игнорирует ошибку.Что произойдет, если DoSomething не сможет сделать то, что должен был сделать?Вы не могли бы знать об этом, и весь асинхронный рабочий процесс просто зависнет.Или, что еще хуже: процесс завершится немедленно (в зависимости от того, как произойдет сбой).

Если у вас есть контроль над DoSomething, я предлагаю вам решить эту проблему.

0 голосов
/ 10 июня 2018

Вы можете попробовать что-то вроде:

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

Если ваша цель заключается в определении обратного вызова в методе, вы можете сделать что-то вроде:

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

Если ваша цель состоит в том, чтобыВ результате использования асинхронного метода в обратном вызове DoSomething вы можете сделать что-то вроде:

let doSomething =

         Async.StartWithContinuations(
         async {
            return result
            },
         (fun result -> Tasks.DoSomething(result)),
         (fun _ -> printfn "Deal with exception."),
         (fun _ -> printfn "Deal with cancellation."))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...