Вернуть асинхронное значение из Fable.Remoting
Вот пример Fable.Remoting на стороне клиента, который печатает результат асинхронной функции.
// Client code (Compiled to Javascript using Fable)
// ============
open Fable.Remoting.Client
let server = Proxy.create<IServer>
async {
let! length = server.getLength “hello”
do printfn “%d” length // 5
}
|> Async.StartImmediate
Как я могу получить length
стоимость?
3 ответа
Я вижу, вы пометили свой вопрос на Elmish, поэтому я предполагаю, что у вас есть Msg
тип определен. Не использовать Async.StartImmediate
или же Async.RunSynchronously
; в Elmish, вы должны использовать Cmd.OfAsync
запланировать отправку сообщения, когда асинхронный блок возвращает значение. Есть четыре функции вCmd.OfAsync
(и те же четыре появляются вCmd.OfPromise
также): either
, perform
, attempt
, а также result
, Я сломаю их для вас, так как их документация еще не совсем готова:
either
: принимает четыре параметра,task
,arg
,ofSuccess
, а такжеofError
,task
это асинхронная функция, которую вы хотите вызвать (типа'a -> Async<'b>
).arg
это параметр типа'a
что вы хотите передатьtask
функция.ofSuccess
является функцией типа'b -> 'Msg
: он получит результат асинхронной функции и должен создать сообщение, предположительно такое, которое включает'b
результат. В заключение,ofError
является функцией типаexn -> 'Msg
: еслиtask
функция выдает исключение, затемofError
будет называться вместоofSuccess
и должен превратить это исключение в эльмийское сообщение, которое может обработать ваш код (предположительно, такое, которое сообщит об ошибке в консоль Javascript или отобразит уведомление с Thoth.Toast или чем-то в этом роде).perform
: любитьeither
но нетofError
параметр. Используйте это, если ваша асинхронная команда не может потерпеть неудачу (что никогда не случается с удаленными вызовами API, поскольку всегда возможно, что сеть отключена или ваш сервер не отвечает), или если вы просто не заботитесь об исключениях и не возражаете против необработанное исключение становится брошенным.attempt
: любитьeither
но нетofSuccess
параметр, поэтомуtask
результат функции будет проигнорирован в случае успеха.result
: это совершенно другое. Это просто принимает один параметр типаAsync<'Msg>
то есть вы передаетеasync
блок, который уже собирается выдать сообщение.
С кодом, который вы написали, вы бы использовали Cmd.OfAsync.result
если вы хотите внести минимальные изменения в свой код, но я бы предложил использовать Cmd.OfAsync.perform
вместо этого (и обновляя его до Cmd.OfAsync.either
как только вы написали некоторый код обработки ошибок). Я покажу вам оба пути:
type Msg =
// ... rest of your messages go here
| GetLength of string
| LengthResult of int
let update msg model =
match msg with
// ... rest of your update function
| GetLength s ->
let usePerform = true
if usePerform then
model, Cmd.OfAsync.perform server.getLength s LengthResult
else
let length : Async<Msg> = async {
let! length = server.getLength s
return (LengthResult length)
}
model, Cmd.OfAsync.result length
| LengthResult len ->
// Do whatever you needed to do with the API result
printfn "Length was %d" len
model, Cmd.none
И если вы использовали either
(что вы действительно должны сделать, как только вы идете в производство), будет третье сообщение LogError of exn
это будет обработано как:
| LogError e ->
printfn "Error: %s" e.Message
model, Cmd.none
и Cmd.OfAsync.perform
строка в коде выше станет:
model, Cmd.OfAsync.either server.getLength s LengthResult LogError
Это правильный способ обработки асинхронных функций в Elmish.
Async является одним из мест, где вы используете return
в F#. Так что вам нужно вернуть значение длины. Также, Async.StartImmediate
возвращается ()
(единица измерения). Используйте что-то еще, например Async.RunSynchronously
если вам нужно извлечь значение. Зависит от того, что вам нужно достичь с ним.
let length =
async {
let! length = async {return String.length "hello"}
do printfn "%d" length // 5
return length
} |> Async.RunSynchronously
length // val it : int = 5
Кстати, вы упоминаете басню. Таким образом, вы можете использовать JS promise
,
Некоторые ресурсы по Async в F#:
Для тех, кто хочет звонить из js кода.
// Client code (Compiled to Javascript using Fable)
// ============
open Fable.Remoting.Client
open Fable.Core // required for Async.StartAsPromise
let server = Proxy.create<IServer>
let len_from_fable () =
async {
let! length = server.getLength “hello”
return length
} |> Async.StartAsPromise
звонок из js
async func() {
let len = await len_from_fable()
print(len)
}
работает в басне 3.0.