Как я могу отменить Cmd.OfAsync при изменении реквизита?

#f# #elmish

#f# #elmish

Вопрос:

Предположим, у меня есть управляемая форма Elmish:

 type Model = 
  {
    Query : string
    IsLoading : bool
    Result : Result<QueryResults, string> option
  }

type Message =
  | UpdateQuery of string
  | ReceivedResults of Result<QueryResults, string>

let update message model = 
  match message with
  | UpdateQuery query ->
    let nextModel =
      {
        model with
          Query = query
          IsLoading = true
      }

    let cmd = 
      Cmd.OfAsync.result (async {
        let! results = Api.tryFetchQueryResults query

        return ReceivedResults results
      })

    nextModel, cmd
  | ReceivedResults results ->
    {
      model with
        IsLoading = false
        Results = Some results
    }, Cmd.none
 

При каждом model.Query изменении он отправляет async запрос. Однако, если запрос уже выполняется, я бы хотел, чтобы он был отменен и заменен новым.

Какой хороший способ сделать это на Elmish?

Ответ №1:

Нет ничего встроенного для поддержки отмены базового XMLHttpRequest .

Однако мы можем создать небольшое оборудование, которое будет поддерживать функциональность, подобную отмене:

Во-первых, дополните ваш Model информацией о текущем ожидающем запросе, а ваш Message — ссылкой (подойдет простая Guid ) на этот запрос

 open System

type Model = 
  {
    Query : string
    IsLoading : bool
    Result : Result<QueryResults, string> option
    PendingQuery: Guid option
  }

type Message =
  | UpdateQuery of string
  | ReceivedResults of Guid * Result<QueryResults, string>
 

Затем предоставьте эти фрагменты данных в вашей update функции

   | UpdateQuery query ->

    let correlationId = Guid.NewGuid()

    let nextModel =
      {
        model with
          Query = query
          IsLoading = true
          PendingQuery = Some correlationId
      }

    let cmd = 
      Cmd.OfAsync.result (async {
        let! results = Api.tryFetchQueryResults query

        return ReceivedResults (correlationId, results)
      })

    nextModel, cmd
 

Наконец, проверьте ожидающий запрос при получении данных с сервера и игнорируйте данные, не соответствующие ссылке

   | ReceivedResults (correlationId, results) ->
    match model.PendingQuery with
    | Some id when id = correlationId ->
      {
        model with
          IsLoading = false
          PendingQuery = None
          Result = Some results
      }, Cmd.none
    | _ -> model, Cmd.none
 

Комментарии:

1. Это игнорирует «устаревшие» запросы, но как мне их отменить?

2. Чтобы отменить, вам нужно получить доступ к базовому XMLHttpRequest и вызвать abort() для этого. Но запрос XMLHttpRequest не отображается в Fable. Удаленный API.

3. У меня есть доступ к cancellation via Async.StartAsPromise , который принимает токен отмены. Мой вопрос в том, как я должен интегрировать / отслеживать токен отмены с Elmish