This is the third day of my participation in the More text Challenge. For details, see more text Challenge
What is the race problem of asynchronous request
First of all, let me explain what race is. Now I have a front end page as shown below, which requests and displays tabled data based on the user’s query conditions.
When the network is normal, we can directly request and display the data, without any technical difficulty at all. However, when the network is unstable, there will be inconsistent query conditions and page display results.
Here’s an example:
- First the user enters “happy” in the description input box and then hits query. This time we call it the first request
- Then the user enters “sad” in the description input box and hits the query. This time we call it the second request
When the network fluctuates, if the result of the second request is returned before that of the first request, the description input box on the page shows “sad”, but the list data displayed on the page is the corresponding result of the “happy” queried in the first request.
How to solve the race problem of asynchronous request
Here are a few ways to solve this problem for your reference (the code below is mainly used to show the idea, and has not been tested).
2.1 Interactive solution
We can solve this problem by adding a global loading mask after a request is initiated, or by disabling the **** query button so that we cannot send new requests before a request is completed.
But this approach has several disadvantages:
- Blocking the interaction
- Queries are triggered by various actions, such as the enter key. There are a lot of points to think about in this case
- Convince product, interaction colleagues (ignore this if you’re particularly good at Battle requirements)
2.2 Canceling a Request
If we can cancel the previous request on each request, we can ensure that the final query results are consistent with the query conditions.
2.2.1 axios
Let’s use Axios’s Cancellation as an example:
const CancelToken = axios.CancelToken; Let source // request function funtion query (keyword) {if (source) {source.cancel(' cancel request '); } source = CancelToken.source(); return axios.post('/list', { keyword }, { cancelToken: If (axios.iscancel (thrown)) {else {// drop error} else {// drop error} }); }Copy the code
In the above code, the previous request is cancelled with source.cancel() before each query.
2.2.2 Cancelable Promise
Of course, not everyone will use Axios as a request library, and a common practice is to customize a cancellations Promise to encapsulate requests. (Note: Promises cannot be cancelled, where you manually set the Promise to the rejected state
), the code is as follows:
Let doCancel // query (keyword) {if (doCancel) {// set the Promise to the rejected state doCancel(' cancel request '); } return new Promise(function(resolve, reject) {// mount the reject method doCancel = reject const XHR = new XMLHttpRequest(); xhr.on("load", resolve); xhr.on("error", reject); xhr.open("POST", '/list', true); Xhr.send (null); If (axios.iscancel (thrown)) {else {// request error}} else {// request error}); }Copy the code
Bluebird’s Cancellation function is a new One if you don’t want to play around with it
2.2.3 RXJS switchMap operator
switchMap
SwitchMap is somewhat similar to Promise. New data distribution cancels the last data distribution
If you are not familiar with RXJS, you can click on this article to learn. Here is a simple example code for swtchMap to handle race asynchronous requests
var btn = document.querySelector('.js-query'); Var inputStream = rX.observable. FromEvent (BTN, 'click').debounceTime(250) SwitchMap (URL => http.get (URL)).subscribe(data => render(data));Copy the code
2.3 Discard useless requests
The last method is the easiest to understand: only the results of the current query condition are processed, and the results of the other query conditions are considered useless requests, which we do not process in the callback function.
Funtion query (keyword) {gobalReqID++ let curReqID = gobalReqID return axios.post('/list', {keyword}).then(res => {if (gobalReqID === curReqID) {return res} else { Return promse.reject (' useless request ')}})}Copy the code
The above code uses an auto-incrementing reqID and closure feature to determine whether the request is useless. For simpler queries, we can directly determine whether the query conditions are consistent.
The resources
- How to solve the race problem of asynchronous requests
- The AXIOS switch route cancellations specified requests and cancellations repeated requests coexist
- How do I terminate a previous promise or prevent its callback from executing?
- RXJS classic scene input search
- RxJS – Give you a silky programming experience
- Building streaming applications – RxJS in detail