从寓言中返回异步值

时间:2019-08-23 04:26:16

标签: f# fable-f# elmish

这里是一个客户端寓言。远程处理示例显示了异步功能的结果。

    // 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 个答案:

答案 0 :(得分:5)

我看到您已经用标记了您的问题,所以我假设您定义了Msg类型。不要使用Async.StartImmediateAsync.RunSynchronously;在Elmish中,一旦异步块返回一个值,就应该使用Cmd.OfAsync安排要调度的消息。 Cmd.OfAsync中有四个功能(同样四个也出现在Cmd.OfPromise中):eitherperformattemptresult 。我将为您分解它们,因为它们的文档还不够完善:

  • either:采用四个参数,taskargofSuccessofErrortask是您要调用的异步函数(类型为'a -> Async<'b>)。 arg是您要传递给'a函数的类型task的参数。 ofSuccess是类型'b -> 'Msg的函数:它将接收异步函数的结果,并应创建一条消息,大概是包含'b结果的消息。最后,ofError是类型为exn -> 'Msg的函数:如果task函数引发异常,则将调用ofError而不是ofSuccess,并且假定将该异常转化为代码可以处理的Elmish消息(可能是将错误记录到Javascript控制台或弹出带有Thoth.Toast或类似内容的通知的消息)。
  • perform:类似于either,但没有ofError参数。如果您的异步命令无法失败(远程API调用永远不会发生这种情况,因为它总是有可能发生网络故障或您的服务器无响应),或者您只在乎异常而不在乎,请使用此选项引发未处理的异常。
  • attempt:类似于either,但没有ofSuccess参数,因此如果成功,task函数的结果将被忽略。
  • result:这是完全不同的。它只需要一个Async<'Msg>类型的参数,即,您将已经产生消息的async块传递给它。

使用您编写的代码,如果您想对代码进行最少的更改,则可以使用Cmd.OfAsync.result,但是我建议使用Cmd.OfAsync.perform代替(并将其升级为{{1} }一旦编写了一些错误处理代码)。我将向您展示两种方式:

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

这是处理Elmish中异步生成功能的正确方法。

答案 1 :(得分:2)

异步是您在F#中使用return的地方之一。因此,您需要返回长度值。同样,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

有关F#中异步的一些资源:

F# Async Guide from Jet

Async Programming

FSharp for Fun and Profit

Microsoft Docs

C# and F# Async

答案 2 :(得分:2)

对于那些想从 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。