F# Fable Fetch 正确地将 Promise 解包到另一个 Promise 类型

F# Fable Fetch Correctly Unwrap Promise to another Promise type

我需要从多个 URL 请求数据,然后使用结果。

我正在使用带有 Fable-Promise 和 Fable-Fetch 库的纯寓言 3。

我已经弄清楚了如何从多个 URL 中获取并将结果组合成一个 Promise,然后我可以用它来更新 UI(多个结果只需要绘制一次)。

但是,如果其中一个提取错误,那么整个事情都会失败。理想情况下,我想使用 tryFetch 然后传播 Result<TermData, None | Exception> 但我似乎没有做任何编译。

我究竟如何使用 tryFetch 然后在 CE 中用第二个 let! 解包结果? (评论说明更多)

module App

open Browser.Dom
open App
open System.Collections.Generic
open System.Text.RegularExpressions
open Fetch
open System

type TermData =
    abstract counts : int []
    abstract scores : int []
    abstract term : string
    abstract allWords : bool

type QueryTerm =
    { mutable Term: string
      mutable AllWords: bool }

let loadSingleSeries (term: QueryTerm) =
    promise {
        let url =
            $"/api/plot/{term.Term}?allWords={term.AllWords}"

        // Works but doesn't handle errors.
        let! plotData = fetch url [] // type of plotData: Response

        // let plotDataResult = tryFetch url []

        // This ideally becomes Promise<Result<TermData, None>>
        // let unwrapped = match plotDataResult with
        //     | Ok res -> Ok (res.json<TermData>()) // type: Promise<TermData>
        //     | Error err -> ??? // tried Error (Promise.create(fun resolve reject -> resolve None)) among others

        let! result = plotData.json<TermData>() // type of result: TermData
        return result 
    }

let dataArrays =
    parsed // type Dictionary<int, TermData>
    |> Seq.map (fun term -> loadSingleSeries term.Value)
    |> Promise.Parallel
    |> Promise.map (fun allData -> console.log(allData))

    // Here we will handle None when we have it

我没有太多 Fable 经验,但如果我正确理解你的问题,我认为以下内容应该有效:

let loadSingleSeries (term: QueryTerm) =
    promise {
        let url =
            $"/api/plot/{term.Term}?allWords={term.AllWords}"

        let! plotDataResult = tryFetch url []

        match plotDataResult with
            | Ok resp ->
                let! termData = resp.json<TermData>()
                return Ok termData
            | Error ex ->
                return Error ex
    }

这里的想法是,如果您遇到错误,您只需在新的 Result 值中传播该错误。这 return 是 Promise<Result<TermData, Exception>>,正如您所要求的。

更新:使用第二个 let!.

修复了 return 类型

我没有 运行 这段代码,但查看文档看起来你需要使用 Promise.catch

let loadSingleSeries (term: QueryTerm) =
    promise {
        let url =
            $"/api/plot/{term.Term}?allWords={term.AllWords}"

        
        let! plotDataResult = 
            fetch url [] 
            |> Promise.map Ok  // Wraps the "happy path" in a Result.Ok
            |> Promise.catch (fun err -> 
                  //handle the error
                  Error err)

        return
             match plotDataResult with
             | Ok res -> ...
             | Error err -> ...
    }

我最终不得不为此使用管道而不是 CE 方法,如下所示:

let loadSingleSeries (term: QueryTerm) =
    let url =
        $"/api/plot/{term.Term}?allWords={term.AllWords}"

    let resultPromise =
        fetch url []
        |> Promise.bind (fun response ->
            let arr = response.json<TermData> ()
            arr)
        |> Promise.map (Ok)
        |> Promise.catch (Error)

    resultPromise

关键是使用 Promise.bind 将获得 Response 的第一个承诺转换为 Promise<TermData> 的承诺。 mapcatch 然后转换为 Promise<Result<TermData, exn>>.