4

刚从 Fable / Elmish / F# 开始,但在完成一个基本概念时遇到了困难......

代码的目标是执行一个简单的 HTTP GET 请求,然后在网页上将 GET 请求的结果发布给用户。想法是使用 FablePromiseasync执行 GET 请求并返回结果,然后可以使用 Elmish 和 Fable 将结果显示在网页上。

我希望网页(本地主机)会输出一个包含 GET 请求响应的文本字符串,而不是我在网页上只得到“[object Promise]”。然而,GET 请求的所需响应会记录在浏览器控制台上。txt如果我在 promise 的正文中(在 之前)发出 printfn 命令,return我还可以在浏览器控制台中看到 GET 请求的结果。

因此:我不知道如何访问承诺的结果。请帮忙,我错过了什么?[Fable][1] 中的示例应用程序创建了一个图像“容器”,并根据 HTTP 获取的结果放置了一个图像,但我需要使用文本(json 字符串)。

这是我的代码:

module App

open Elmish
open Fable.React
open Elmish.React
open Fable.React.Props
open Fable.Import
open Fable.PowerPack

open Fetch

// CallAPI
let getMe = promise { 
                    let! res = fetch "https://reqres.in/api/users/2" [] 
                    let! txt = res.text()              
                    // Access your resource here
                    Browser.console.log txt 
                    return txt
                    }

//State
type MyInputString = 
     {Response : string}


// Message
type Message = 
           |ShowResponse
           |Error

let getFromAPI  = 
     getMe |> sprintf "%A" 


//Update
let update msg model = 
           match msg with 
           |ShowResponse -> {Response = getFromAPI }
           |Error -> {Response = "Errrrrr"}


//Helper function
let text(content:string) : ReactElement = unbox content
//Helper function
let init() = {Response = ""}


//View
let view model dispatch = 
    div
     []
      [
          div [] [ input[ Type "text"] ]
          button [ OnClick (fun _ -> dispatch ShowResponse ) ] [ text "Show Me"]
          div [] []
          div [] [text (model.Response)]
      ]

Program.mkSimple init update view
|> Program.withReactSynchronous "werk"
|> Program.withConsoleTrace
|> Program.run ```




  [1]: https://fable.io
4

2 回答 2

5

如果您按照 elmish 模式执行此操作,则可以执行以下操作。这可能会产生一些错误,但应该足以让您继续前进:

使用 mkProgram 因为它让您可以灵活地指定 Cmd 回调

Program.mkProgram init update view
    |> Program.withReact "elmish-app"
    |> Program.withConsoleTrace
    |> Program.run

确保您的模型和消息之间有区别

type Model = {
    UserMessage: string
    ErrorMessage: string
}

添加消息类型以接收承诺结果并处理错误:

type Message = 
        | Loading
        | ShowResponse of Result<string,string>
        | Error of exn

由于签名已更改,您的 init 现在应该返回模型和 Cmd(请参阅下面的更新):

let init() = { UserMessage= "" ; ErrorMessage= "" } , Cmd.ofMsg Loading

更改您的更新功能,签名现在已更改,因为我们正在使用 mkProgram。请注意,当您调用 Cmd.ofPromise 时,您将 ShowResponse 和 Error 消息作为回调传递 - 如果您的 Promise 代码中发生任何不好的事情,则在成功和错误时显示响应。

//update signature has changed from
msg -> model -> model
//to:
msg -> model -> (model*Cmd<msg>)

let update msg model = 
            match msg with 
            | Loading -> 
                { model with UserMessage= "Loading" }, 

                    Cmd.ofPromise (getMe ) () ShowResponse Error
            | ShowResponse  resp -> 
                match resp with
                    | Ok str -> 
                        { model with UserMessage = str }, Cmd.none
                    | Error str -> 
                        { model with ErrorMessage = str }, Cmd.none                     
            | Error exn ->  { model with ErrorMessage = string exn }, Cmd.none
于 2019-12-04T14:04:32.433 回答
4

Promise 本质上是一种命令式的构造——你只能通过副作用来获得它们的价值。它们不像 C# 中的任务,它也可以同步产生一个值。这意味着您的模型必须有两个与 getMe 相关的状态:等待 getMe 完成时的状态,以及 getMe 完成时的状态。每个状态都有自己的消息和自己的更新案例。第一种情况的处理程序将使用 Cmd.ofSub 开始 getMe,并确保当 Promise 完成时,它具有发送第二条消息的副作用,该消息将在 UI 中显示 Promise 的结果。

所以除了ShowResponse和Error之外,还需要第三条消息MakeRequest。下面的代码只是一个草图(未经测试),但说明了这一点:

// Message
type Message = 
  | MakeRequest
  | ShowResponse of string
  | Error

let getMe dispatch = 
   promise { 
     let! res = fetch "https://reqres.in/api/users/2" [] 
     let! txt = res.text()              
     // Access your resource here
     Browser.console.log txt 
     dispatch (ShowResponse txt)
   }

//Update
let update msg model = 
  match msg with 
  | MakeRequest -> { model with Response = "Wait..." }, 
                     Cmd.ofSub(fun dispatch -> getMe dispatch |> Promise.start)
  | ShowResponse msg -> { model with Response = msg }, Cmd.empty
  | Error -> { model with Response = "Errrrrr"}, Cmd.empty
于 2019-12-04T22:14:30.473 回答