current position:Home>Do you know how to implement concurrency control and failure retry of front-end requests?

Do you know how to implement concurrency control and failure retry of front-end requests?

2021-08-27 03:18:31 Jiaxin cansiny

This is my participation 8 The fourth of the yuegengwen challenge 3 God , Check out the activity details :8 Yuegengwen challenge

When brushing the surface , I see a problem that requires the concurrency control of requests and failure retry , Just my own project ( One Novel crawler , If you are interested, you can have a look at ) These two technologies are also used in , So sort out the implementation methods here

concurrency control

In some scenes , The front end needs to send a large number of network requests in a short time , At the same time, it can't occupy too much system resources , This requires concurrency control for requests . The request here may be the same interface , It can also be multiple interfaces , Generally, we have to wait for all interfaces to return before unified processing . In order to improve efficiency , We want to empty the position as soon as a request is completed , Then launch a new request . Here we can make comprehensive use of Promise Two tools and methods to achieve the goal , Namely race and all.

In all promise None reject Under the circumstances :

Promise.all Will be in all promise all resolve Return with all promise resolve An array of results

Promise.race Will return fastest resolve Of promise Value

The code implementation is as follows :

 /**   *   * @param {number} limit  Concurrency limit    * @param {(() => Promise<any>)[]} requests  An array containing all requests    * @returns {Promise<any[]>}  Result array    */
 async function concurrentControl(limit, requests) {
   //  Store all asynchronous tasks 
   const res = []
   //  Store asynchronous tasks in progress 
   const executing = []

   for (const request of requests) {
     const p = Promise.resolve().then(() => request())
     //  Save the new asynchronous task 
     res.push(p)
     //  When limit When the value is less than or equal to the total number of tasks , Concurrent control 
     if (limit <= requests.length) {
       //  When the task is done , Remove completed tasks from the executing tasks array 
       const e = p.then(() => executing.splice(executing.indexOf(e), 1))
       //  Save the executing asynchronous task 
       executing.push(e)
       if (executing.length >= limit) {
         //  Waiting for faster tasks to complete 
         await Promise.race(executing)
       }
     }
   }
   return Promise.all(res)
 }
 Copy code 

Simply analyze the process ,

First, traverse all of the requests, Join in res in , When limit <= requests.length,limit When the value is less than or equal to the total number of tasks , Concurrent control

Here's a piece of code to understand

p.then(() => executing.splice(executing.indexOf(e), 1))

In fact, it is equivalent to the following

 const e = p.then(fn);
 executing.push(e);
 // p resolve  After execution  fn
 () => executing.splice(executing.indexOf(e), 1)
 Copy code 

Next, when the request being executed is greater than limit when , You need to call await Promise.race(executing) Wait for a request to complete , Last use Promise.all(res) Return all results

Test code

 let i = 0
 function generateRequest() {
   const j = ++i
   return function request() {
     return new Promise(resolve => {
       console.log(`r${j}...`)
       setTimeout(() => {
         resolve(`r${j}`)
       }, 1000 * j)
     })
   }
 }
 const requests = new Array(4).fill('').map(() => generateRequest())
 
 async function main() {
   const results = await concurrentControl(2, requests)
   console.log(results)
 }
 main()
 Copy code 

Request to retry

In some scenarios where you need to ensure the success of the request , We may also need the ability to request a retry , For example, the request section in my project failed , Need to ask again

Next, let's look at the implementation of request retry

Compared with the implementation of request retry, concurrency control is much simpler

async function retry(request, limit, times = 1) {
  try {
    const value = await request()
    console.log(' To be successful ')
    return value
  } catch (err) {
    if (times > limit) {
      return err
    }
    console.log(` request was aborted , The first  ${times}  Retries ...`)
    return retry(request, limit, ++times)
  }
}
 Copy code 

Just when you catch an error , Recursively call retry Function is OK , When the maximum number of retries is exceeded , Just return the error

Test code

 let i = 0
 function generateRequest() {
   const j = ++i
   return function request() {
     return new Promise((resolve, reject) => {
       setTimeout(() => {
         Math.random() > 0.5 ? reject(`err`) : resolve('success')
       }, 1000 * j)
     })
   }
 }
 
async function retry(request, limit, times = 1) {
  try {
    const value = await request()
    console.log(' To be successful ')
    return value
  } catch (err) {
    if (times > limit) {
      return err
    }
    console.log(` request was aborted , The first  ${times}  Retries ...`)
    return retry(request, limit, ++times)
  }
}

retry(generateRequest(), 3).then(console.log)
 Copy code 

Reference article

front end API Various operations requested

copyright notice
author[Jiaxin cansiny],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210827031828549G.html

Random recommended