Best practices for front-end processing concurrency

What is concurrency?

Because js is single-threaded, front-end concurrency refers to sending multiple data requests in a very short time, such as sending ajax in a loop.

Take a simple example:

The following piece of code is a request for regular mountstage execution:

useEffect(async () => {
    
    
    console.time();
    await TaskBizService.querySpyTaskSummary();
    await TaskBizService.querySpyTask();
    console.timeEnd();
    
    // time: 300ms
}, [])

Replace it with this:

useEffect(() => {
    
    
    console.time();
    Promise.all([
        TaskBizService.querySpyTaskSummary(),
        TaskBizService.querySpyTask(),
    ]).then((res) => {
    
    
        console.timeEnd();
    });

    // time: 120ms
}, [])

It can be seen that there is a lot of room for performance optimization and differences. If multiple requests do not have mutual data dependencies (requirements dependent) during page rendering, directly using parallel requests will speed up the time for data display in the page. The two demos are simultaneously It also involves some knowledge of the event loop.

Therefore, you can also find some optimizable requests in the page and convert them to parallel, which is very helpful for the optimization of the first screen rendering.

Promise.all

Promise.allConcurrent processing can be used . When promiseall are successful, it will go .thenand get all the values promise​​passed in resolve.

Take a look at this code:

let p1 = new Promise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
  	resolve('request1 end')
  }, 1000);
})

let p2 = new Promise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
  	resolve('request2 end')
  }, 3000);
})

console.time(); // 开始计时
Promise.all([p1, p2])
.then(result => {
    
    
  console.timeEnd(); // default: 3.2s
  console.log(result); // (2) ['request1 end', 'request2 end']  
})

If Promise.allany instance fails, the entire concurrency will hang up.

Like this code:

let p1 = new Promise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
  	resolve('request1 end')
  }, 1000);
})

let p2 = new Promise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
  	reject('request2 fail')
  }, 3000);
})

console.time(); // 开始计时
Promise.all([p1, p2])
.then(result => {
    
    
  // 不会走到这一步
  console.timeEnd(); // default: 3.2s
  console.log(result); 
})

Summarize

  1. Promise.allWhen dealing with concurrency, if there is a promisefailure, it will not go to .then, but if you only need to execute other side-effect codes after all asynchronous executions are completed, you can write the code in to .finallyimplement it.

  2. But if you need to get the value passed promiseby the instance after an asynchronous failure , it will not work.resolve

Promise.allSettled

Promise.allFunctions that cannot be realized in , can be realized by using . Promise.allSettledIn Promise.allSettled, when one of them promisefails to execute, it will continue to go .then, and can get the passed resolve、rejectvalue at the same time. You can use the callback value to judge the request result of the interface to do two things. processing.

The code modification is also very simple, as follows:

let p1 = new Promise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
  	resolve('request1 end')
  }, 1000);
})

let p2 = new Promise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
  	resolve('request2 end')
  }, 3000);
})

console.time(); // 开始计时
Promise.allSettled([p1, p2])
.then(result => {
    
    
  console.timeEnd(); // default: 3.2s
  console.log(result); // (2) ['request1 end', 'request2 end']  
})

When all the results of the request are complete resolve, Promise.allSettledthere Promise.allis not much difference with the request, but catcha compensation is made for the case.

At the same time Promise.allSettled, the compatibility did not Promise.allcome well.

Summarize

  1. Promise.allSettledWhen dealing with concurrency, you are not afraid of asynchronous execution failure, and you will continue to go .then, which perfectly solves Promise.allthe situation that you cannot get the value in the case of concurrency failure.

  2. The only shortcoming is that it is Promise.alla little worse than compatibility, and there are two more unsupported browsers, Firefox for Android and Samsung Internet browser.

async/await

async/awaitCan it handle concurrency? The answer is yes, but the code will look bloated and difficult to read. Let's take a look at the conventional async/awaitasynchronous processing scheme first.

code show as below:

let getData1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request1 end')
    }, 2000);
  })
};

let getData2 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request2 end')
    }, 3000);
  })
}
let syncFn = async () => {
    console.time(); // 开始计时
    
    const data1 = await getData1();
    const data2 = await getData2();
    
    console.timeEnd(); // default: 5.8s
    console.log(data1, data2); // request1 end, request2 end
}

syncFn();

It can be seen from the time-consuming async/awaitthat asynchronous blocking is caused, and serial (dependency) requests are actually used, which is also the most common processing method in ordinary projects. Next, let's transform the async/awaitparallel request scheme.

let getData1 = () => {
    
    
  return new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
    
      resolve('request1 end')
    }, 2000);
  })
};

let getData2 = () => {
    
    
  return new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
    
      resolve('request2 end')
    }, 3000);
  })
}
let syncFn = async () => {
    
    
    console.time(); // 开始计时
    
    const p1 = getData1();
    const p2 = getData2();

    const data1 = await p1;
    const data2 = await p2;
    
    console.timeEnd(); // default: 3.8s
    console.log(data1, data2); // request1 end, request2 end
}

syncFn();

Summarize

  1. If it is installed axiosor other request libraries, in the case that these libraries themselves provide APIs that support concurrency, for example axios.all、axios.spread, it is recommended to use these APIs provided by them, because as a library with millions of downloads, these APIs provided take into account Boundary conditions and compatibility are definitely better than other native methods.

  2. In the absence of these libraries, it is recommended to use them Promise.allSettled. If you like, you can async、awaitalso . There is no difference in the results. It depends on personal preference. But in my opinion, the way async、awaitof .

Guess you like

Origin blog.csdn.net/m0_46995864/article/details/129420378