避免 Elm 中的 Http 竞争条件
Avoid Http Race Condition in Elm
假设我们有一个文本输入字段,每次更改其内容时,我们都会向搜索发送一个 Http 请求 API。现在,我们无法保证 Http 响应会按照我们发送请求的相同顺序返回到 elm。
确保我们对对应于最新请求而不是最新响应的响应做出反应的最简单方法是什么?可能对应于过时的搜索字符串?有没有一种简单的方法可以将查询字符串附加到 Elm 的 http 效果返回的消息中?或者我们可以通过任何其他方式 link 对触发它的请求的响应?
如果可能,我想避免在搜索响应中包含查询 API。另一种补救方法是去抖动搜索,但这只会降低使用错误响应的可能性,而我们想消除它。
感谢您的帮助!
示例:
import Html
import Html exposing (..)
import Html.Events exposing (onClick, onInput)
import Http
import Json.Decode as Decode
main = Html.program
{ init = ( { searchText = "", result = "" }, Cmd.none )
, update = update
, subscriptions = (\model -> Sub.none)
, view = view
}
type alias Model =
{ searchText : String
, result: SearchResult
}
type alias SearchResult = String
type Msg
= NewSearchText String
| ReceivedResponse (Result Http.Error SearchResult)
update msg model =
case msg of
NewSearchText newText ->
( { model | searchText = newText}
, getSearchResult newText
)
ReceivedResponse (Result.Ok response) ->
( { model | result = response }
, Cmd.none
)
ReceivedResponse (Result.Err error) ->
Debug.crash <| (toString error)
getSearchResult : String -> Cmd Msg
getSearchResult query =
let
url = "http://thebackend.com/search?query=" ++ query
request : Http.Request SearchResult
request = Http.get url Decode.string
in
Http.send ReceivedResponse request
view model =
div []
[ Html.input [onInput (\text -> NewSearchText text)] []
, Html.text model.result
]
是的,可以将查询字符串附加到响应中。首先,扩充消息类型以处理额外数据:
type Msg
= NewSearchText String
| ReceivedResponse String (Result Http.Error SearchResult)
然后,更改您的 Http.send
调用以将查询文本附加到 ReceivedResponse
消息:
Http.send (ReceivedResponse query) request
最后,在您的 update
中,在结果 Msg
:
的模式匹配中获取查询
case msg of
ReceivedResponse query (Ok response) ->
...
ReceivedResponse query (Err err) ->
...
为什么这样做有效?
Http.send
函数的第一个参数可以是使用 Result Http.Error SearchResult
并将其转换为 Msg
的任意函数。在您的原始代码中,该函数只是 ReceivedResponse
,即 Msg 构造函数。当更新 Msg 类型以便 ReceivedResponse
接受 两个 个参数时,ReceivedResponse
构造函数变成一个柯里化的双参数函数,而 ReceivedResponse "some query here"
是接受 Result
和 returns 一个 Msg
.
的单参数函数
这是一种方法:
向您的模型添加两个整数:
requestsSent : Int
-- 发出的请求数。
lastReceived : Int
-- 您处理的最新请求。
修改 ReceivedResponse
以将 Int 作为第一个值:
| ReceivedResponse Int (Result Http.Error SearchResult)
现在,无论何时发出请求,都会在模型中将 requestsSent
递增 1,并通过部分应用 ReceivedResponse
:
来递增 "tag" 请求
Http.send (ReceivedResponse model.requestsSent) request
在您的 update
函数中,检查 ReceivedResponse
中的 Int
是否大于 lastReceived
。如果是,则处理它,并将 lastReceived
的值设置为该响应的 Int。如果不是,请丢弃它,因为您已经处理了一个较新的请求。
假设我们有一个文本输入字段,每次更改其内容时,我们都会向搜索发送一个 Http 请求 API。现在,我们无法保证 Http 响应会按照我们发送请求的相同顺序返回到 elm。
确保我们对对应于最新请求而不是最新响应的响应做出反应的最简单方法是什么?可能对应于过时的搜索字符串?有没有一种简单的方法可以将查询字符串附加到 Elm 的 http 效果返回的消息中?或者我们可以通过任何其他方式 link 对触发它的请求的响应?
如果可能,我想避免在搜索响应中包含查询 API。另一种补救方法是去抖动搜索,但这只会降低使用错误响应的可能性,而我们想消除它。
感谢您的帮助!
示例:
import Html
import Html exposing (..)
import Html.Events exposing (onClick, onInput)
import Http
import Json.Decode as Decode
main = Html.program
{ init = ( { searchText = "", result = "" }, Cmd.none )
, update = update
, subscriptions = (\model -> Sub.none)
, view = view
}
type alias Model =
{ searchText : String
, result: SearchResult
}
type alias SearchResult = String
type Msg
= NewSearchText String
| ReceivedResponse (Result Http.Error SearchResult)
update msg model =
case msg of
NewSearchText newText ->
( { model | searchText = newText}
, getSearchResult newText
)
ReceivedResponse (Result.Ok response) ->
( { model | result = response }
, Cmd.none
)
ReceivedResponse (Result.Err error) ->
Debug.crash <| (toString error)
getSearchResult : String -> Cmd Msg
getSearchResult query =
let
url = "http://thebackend.com/search?query=" ++ query
request : Http.Request SearchResult
request = Http.get url Decode.string
in
Http.send ReceivedResponse request
view model =
div []
[ Html.input [onInput (\text -> NewSearchText text)] []
, Html.text model.result
]
是的,可以将查询字符串附加到响应中。首先,扩充消息类型以处理额外数据:
type Msg
= NewSearchText String
| ReceivedResponse String (Result Http.Error SearchResult)
然后,更改您的 Http.send
调用以将查询文本附加到 ReceivedResponse
消息:
Http.send (ReceivedResponse query) request
最后,在您的 update
中,在结果 Msg
:
case msg of
ReceivedResponse query (Ok response) ->
...
ReceivedResponse query (Err err) ->
...
为什么这样做有效?
Http.send
函数的第一个参数可以是使用 Result Http.Error SearchResult
并将其转换为 Msg
的任意函数。在您的原始代码中,该函数只是 ReceivedResponse
,即 Msg 构造函数。当更新 Msg 类型以便 ReceivedResponse
接受 两个 个参数时,ReceivedResponse
构造函数变成一个柯里化的双参数函数,而 ReceivedResponse "some query here"
是接受 Result
和 returns 一个 Msg
.
这是一种方法:
向您的模型添加两个整数:
requestsSent : Int
-- 发出的请求数。lastReceived : Int
-- 您处理的最新请求。
修改 ReceivedResponse
以将 Int 作为第一个值:
| ReceivedResponse Int (Result Http.Error SearchResult)
现在,无论何时发出请求,都会在模型中将 requestsSent
递增 1,并通过部分应用 ReceivedResponse
:
Http.send (ReceivedResponse model.requestsSent) request
在您的 update
函数中,检查 ReceivedResponse
中的 Int
是否大于 lastReceived
。如果是,则处理它,并将 lastReceived
的值设置为该响应的 Int。如果不是,请丢弃它,因为您已经处理了一个较新的请求。