发送 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 中给出 3
,pid
将作为 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
我有一种情况需要同步发送两个 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 中给出 3
,pid
将作为 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