Scene description

A query interface, enter the request data immediately (but at this point the data load slow), and don’t see a loading data from the interface (UI) temporarily not considered here, here as a “request” 】 【 so users can input your own query conditions, then click on the search, here as “request 2 】 【 at this time because of query conditions, So “Request two” returns very quickly, and coincidentally, after “Request two” returns, when the user is ready to select a record to operate on, “Request one” also returns, and the result of “Request one” overwrites “Request two”. If the user is not paying attention, the record he actually selects is inconsistent with what he expected.

The solution

Because the project uses AXIos as the Ajax request tool, you can use its cancelToken feature to manually cancel the request

Version: “axios”: “^0.20.0”

If that were the end, there would be no need to write this article. Because a lot of unexpected questions came to me along the way

The final problem-free code (pay attention to the comments, don’t step on the same hole again)

const httpRequest = axios.create()

httpRequest.interceptors.response.use(
  resp= >{ 
  /* This omits much of the project's custom logic [note: this is executed only when the back-end interface is responding (even 500, or any other error response is responsive)] */ 
  },
  error= >{
    // If the network is disconnected or disconnected, or the request is cancelled by calling the cancel method
    Return promise.reject (error). If no information is returned, then the Promise logic will be used instead of catch logic. Return promise.reject (error) */
    if (error.constructor && error.constructor.name === 'Cancel') {
      /* You can determine that the current error is Cancel. Of course, you can also define an exception according to the rules of your own development system, and then the interface will handle the exception when it catches the Cancel
    }
    return Promise.reject(error)
  }
)

function buildCancelToken () {
  return axios.CancelToken
}

// Cache the previous request's cancel, which cancels the previous pending request
let cancel = null
// This CancelToken can be shared throughout the project
const CancelToken = buildCancelToken()

function ajaxMethod(reqData){
  /* Each time a request is made, check for the cancel and cancel the previous unfinished request if it exists
  if (cancel) {
    // Cancels the previous incomplete request
    cancel()
  }
  // Initiate a new request
  return httpRequest({
    url: 'xxxxxxxxxxxxxxxx'.method: 'post'.cancelToken: new CancelToken(c= > {
      // Record cancel, which cancels the request before the next request (if the request has not completed by then)
      cancel = c
    }),
    data: reqData
  })
  /* In particular, do not manually set cancel to NULL in the Promise's finally block or anywhere else, as this may cause the expected control to fail, because you may manually set Cancel to null just as N requests come in. Cause both of them to get null cancel at the same time, and your desired control is invalidated */
}
Copy the code

If all ajax interfaces require this logic. So we can do this

Note: The removePending method checks whether the request exists, then cancels the request, and finally removes pending. I don’t know if there is any problem with this logic. If there is, execute the cancel method once you get it, but do not remove the cancel from pending.

//cancelToken.js
// Declare a Map to store the identity and cancel functions for each request
const pending = new Map(a)/** * Add request *@param {Object} config 
 */
export const addPending = (config) = > {
  const url = [
    config.method,
    config.url
  ].join('&')
  config.cancelToken = config.cancelToken || new axios.CancelToken(cancel= > {
    if(! pending.has(url)) {// If there is no current request in Pending, add it
      pending.set(url, cancel)
    }
  })
}
/** * Remove request *@param {Object} config 
 */
export const removePending = (config) = > {
  const url = [
    config.method,
    config.url
  ].join('&')
  if (pending.has(url)) { // If the current request identifier exists in pending, cancel the current request and remove it
    const cancel = pending.get(url)
    cancel(url)
    pending.delete(url)
  }
}
/** * Clears requests in pending (called when a route jumps) */
export const clearPending = () = > {
  for (const [url, cancel] of pending) {
    cancel(url)
  }
  pending.clear()
}
Copy the code
Import {addPending,removePending} from "./cancelToken"// cancelToken // create axios instance const service = axios.create({ headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'the XMLHttpRequest'}}) / / request interceptor service. Interceptors. Request. Use (config = > {removePending (config) / / before the start of the request, AddPending (config) // Add the current request to pending return config}, Error => {// Do something with request error console.log(error) // for debug promise.reject (error)}) // Respone interceptor Service. The interceptors. Response. Use (response = > {removePending (response) / / at the end of the request, Remove this request promise.relove (response)}, Error => {if (axios.iscancel (error)) {// Handle manual cancel console.log(' This is manual cancel ')} return promise.reject (error)})Copy the code