Why do you want to write this article?

I think you’ve read a lot of articles about handling repeated requests, most of which are divided into two versions: the return of repeated requests before the response is returned, and the use of throttling/shaking to indirectly avoid frequent user operations. Recently, in the process of using, I found that these two versions are somewhat limited in some scenarios.

2. Problem scenario

In the figure above, my H5 page will display the business card component at the top and bottom. The information for these business cards is obtained through an interface, and when this component is initialized on the current page, the request is repeated twice.

There are several choices:

1. Do nothing to repeat requests.
  • Disadvantage 1: Unnecessary resource waste, increase the server pressure
  • Disadvantage 2: There is a limit on the number of concurrent HTTP requests in the browser. If there are many requests on the first screen of the page and there is no hierarchical loading, the request is easily blocked, affecting the user to see the main content in the first time
2. Return the repeated request. This is also what some articles do, but there is a limitation to this approach, which is to simply assume that the following repeated requests areInvalid request.
  • Invalid request scenario: The user clicks a button to query or save the request, and the next click is basically counted until the request result is returnedInvalid requestThis request should be blocked. Of course, you can get around this by adding throttling/stabilization to the button
  • Why this does not apply to the current scenario: Both business card components require data to render, and if the second repeated request is returned, the business card of one of the components will have no data.
3. Pull the request out of the component, put it on the parent business page, and pass it to the component as props.
  • Benefit: With only one request, two components can share the same data.
  • Limitations: Only applicable when used by a single business page. In fact, this component is used by many business pages. Even if the requested function is derived from the public API, it must be called once at the initialization of each business page, and then passed into the component as props.

Third, the solution

core idea

  • Initialize an array of handleList
  • Before the request is sent, it determines whether the request is a duplicate request based on whether the incoming parameters are the same
    • Non-repetitive requests: Add the parameters of the change request and the Promise returned by the request to the array
    • Repeat request: Use find to return the corresponding Promise directly
  • When the request is complete, remove the previously added request information from the elist.

This scheme can be used for anything, whether using Axios, JQ, FETCH, or request. Here to write the principle of implementation, when using the corresponding code directly into the corresponding request time can be.

Code sample

let handleList = [] // Request list
/** * Mock request *@author waldon
 * @date 2020/6/9 * /
const httpRequest = () = > {
  return new Promise((resolve) = > {
    setTimeout(() = > {
      resolve('Request succeeded with timestamp:The ${new Date().getTime()}`)},1000)})}/** * Request related processing *@author waldon
 * @date 2020/6/9
 * @param {String} url -
 * @param {Object} requestObj- Request parameter *@returns {Promise} - Requested promise */
function requestTest(url, requestObj = {}) {
  Json. stringify is sufficient for serialization comparison, since the input parameters generally do not involve complex types
  // There is a limitation that a change in the order of the input parameters can affect the judgment, but this particular change is usually not present in repeated requests
  Lodash also has a similar API for recursion comparison
  const sameHandle = handleList.find(
    (item) = > item.url === url && JSON.stringify(item.requestObj) === JSON.stringify(requestObj)
  )
  if (sameHandle) {
    // Return the promise of the previous request directly upon the same request
    console.log('There is a duplicate request, directly return')
    return sameHandle.handle
  }
  const handle = new Promise((resolve, reject) = > {
    httpRequest()
      .then((res) = > {
        resolve(res)
      })
      .catch((err) = > {
        reject(err)
      })
      .finally(() = > {
        // Remove the corresponding request regardless of the result of the request
        handleList = handleList.filter(
              (item) = >item.url ! == url &&JSON.stringify(item.requestObj) ! = =JSON.stringify(requestObj)
            )
      })
  })
  handleList.push({ url, requestObj, handle })
  return handle
}

/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * I am a gorgeous line Begin to use * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
const params = {
  name: 'waldon'
}
requestTest('/ajax/sameUrl', params).then((res) = > {
  console.log('First request result', res)
  console.log(` handleList: `, handleList)
})
requestTest('/ajax/sameUrl', params).then((res) = > {
  console.log('Repeat request result', res)
  console.log(` handleList: `, handleList) // There is always only one request in the request list
  setTimeout(() = > {
    console.log('Request completed handleList:', handleList) // The request for the complete handleList will be cleared
  }, 100)})setTimeout(() = > {
  // We deliberately delayed the request by 500ms, because we set the interface to return for 1s, so we should get the same result
  requestTest('/ajax/sameUrl', params).then((res) = > {
    console.log('Repeat request result', res)
    console.log(` handleList: `, handleList)
  })
}, 500)

Copy the code

The output

If there is a duplicate request, directly return the result of the first request. The request is successful, and the timestamp is:1621650375540HandleList: [{url: '/ajax/sameUrl'.requestObj: { name: 'waldon' },
    handle: Promise { 'Request succeeded with timestamp: 1621650375540'}}}}1621650375540HandleList: [{url: '/ajax/sameUrl'.requestObj: { name: 'waldon' },
    handle: Promise { 'Request succeeded with timestamp: 1621650375540'}}}}1621650375540HandleList: [{url: '/ajax/sameUrl'.requestObj: { name: 'waldon' },
    handle: Promise { 'Request succeeded with timestamp: 1621650375540' }
  }
]
请求完成后的handleList: []
Copy the code

Code address codepen

Codepen. IO/waldonUB/PE…

Pay attention to the point

  • Do not add or delete data in response. Because the object reference address in the Promise returned by repeated requests is the same, changing it will pollute the data. In special cases, the response results can be shallow copied and processed, or the corresponding assertion can be added.
  • When handling repeated requests, it is a good idea to prompt them in the log, and annotate the reason and usage scenario in the component to avoid errors by others
  • Do a good job in extreme cases, request failure processing, set the valid time to empty and remove the request information, to avoid the closure accumulation of too much useless request information caused by memory leak.