发送http请求时的竞争条件

时间:2017-03-11 04:35:18

标签: race-condition elm

我有一种情况需要同步发送两个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)

    ...

我测试了好几次。如果服务器在第一篇帖子中提供3,则pid会以0的形式发送,但如果我再次提交,pid将以3的形式发送例如,服务器的答案4将被忽略。

如何让帖子等待分配值?

1 个答案:

答案 0 :(得分:4)

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

使用{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