发送 http 请求时的竞争条件

Race-condition while sending http request

我有一种情况需要同步发送两个 post 请求,第二个请求取决于第一个请求的响应,问题是第二个请求甚至在第一个响应分配之前就已发送,从而发送错误信息:

update : msg -> model -> (model, Cmd msg)
update msg m =
    case msg of

    ...

    Submit -> (m,
        send FirstResp <| post "/resource1"
            (jsonBody <| encoderX m) int)
    FirstResp (Ok x) -> ({m | pid = x},
        send SecondResp <| post "/resource2"
            (jsonBody <| encoderY m) int)

    ...

我测试了好几次。如果服务器在第一个 post 中给出 3pid 将作为 0 发送,但如果我再次提交,pid 将作为 3 和来自服务器的答案,例如 4,将被忽略。

如何让post等待分配值?

由于 elm 中的数据结构是不可变的 {m | pid = x} 不会更改 m 但 returns 一条新记录。因此,当您将模型传递给第二个请求时,您没有更新模型。

使用 {m | pid = x} 两次可以得到您想要的结果(但不是很漂亮)

FirstResp (Ok x) -> ({m | pid = x},
    send SecondResp <| post "/resource2"
        (jsonBody <| encoderY {m | pid = x}) int)

您可以在发送请求之前使用 let in 将新模型存储在变量中。如果您现在修改模型,您只需查看一个地方。

FirstResp (Ok x) -> 
    let
        newM = {m | pid = x}
    in
        (newM, send SecondResp <| post "/resource2"
        (jsonBody <| encoderY newM) int)

如果您不需要模型中第一个请求的结果,更好的解决方案是使用 Task.andThen 链接请求。有了这个,您就不需要 2 个单独的消息(FirstResp、SecondResp)。

request1 m = 
    post "/resource1" (jsonBody <| encoderX m) int)
        |> Http.toTask

request2 m =
    post "/resource1" (jsonBody <| encoderX m) int)
        |> Http.toTask

Submit -> 
    ( m
    , request1 m
        |> Task.andThen request2
        |> Task.attempt Resp
    )

Resp (Ok res2) -> 
    -- res2 is the result of your request2

如果您需要这两个结果,您可以将它们映射到一个元组中并在更新函数中提取它。

Submit -> 
    ( m
    , request1 m
        |> Task.andThen 
            (\res1 -> request2 res1
                |> Task.map ((,) res1) 
            )
        |> Task.attempt Resp
    )

Resp (Ok (res1, res2) -> 
    -- use res1 and res2 

Elm packages - Task