简体   繁体   中英

How can I cancel Cmd.OfAsync as props change?

Suppose I have a controlled Elmish form:

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

Every time model.Query changes, it sends off an async request. However, if there is already a request in progress, I would like that to be cancelled and replaced with the new one.

What is a good way to do this in Elmish?

There is nothing built in to support cancellation of the underlying XMLHttpRequest .

We can, however, build some small machinery that will support cancellation-like functionality:

First, augment your Model with information about the current pending query, and your Message with a link (a simple Guid will do) to that query

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>

Next, provide these pieces of data in your update function

  | 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

Finally, check the pending query when receiving data from the server and ignore stuff not matching the link

  | 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM