Location>code7788 >text

How to avoid overwriting the latest request with data from older requests

Popularity:963 ℃/2024-09-19 11:36:03

My blog address:How to avoid data from old requests overwriting the latest request - Mosquito's Front End Blog

In retrieval scenarios, it is often the case that requests with different retrieval conditions are initiated for the same interface, and if the response to the previous request is slow, it may overwrite the results of our later initiated request.

If we first launch a search request, the parameter is A; this request is not yet over, we launched a search request with parameter B; perhaps due to network reasons or back-end service processing reasons, the request with parameter B launched after the first response, and then we show the data to the page; after a while before the launch of the search request with parameter A also returned results. But in fact, the response result of parameter B is the latest data we need to display.

So how can this be avoided?

1. Requests for locking

You can add loading to the button, input box, etc. that initiated the request, or globally, so that the loading is canceled only after the result of the previous request is obtained, and the user is allowed to initiate the next request.

const App = () => {
  const [loading, setLoading] = useState(false);

  const request = async (data) => {
    if (loading) {
      // If the request is still pending,then a new request cannot be initiated
      return;
    }
    setLoading(true);
    const result = await axios("/api", { data, method: "post" });
    setLoading(false);
  };

  return (
    <div className="app">
      <Form disabled={loading} onFinish={request}>
        <>
          <Input />
        </>
        <Button htmlType="submit" loading={loading}>
          look for sth.
        </Button>
      </Form>
    </div>
  );
};

By controlling directly with the source, there is no such thing as multiple requests in parallel, and it doesn't matter who overrides who.

2. Anti-shake

Generally applied in pure input box search function, the search is initiated after the user stops typing for a certain period of time, so that the time interval between two retrieval requests can be increased.

const App = () => {
  const request = async () => {};

  return (
    <div className="app">
      {/* Stop Input700mspost-trigger request */}
      <input onInput={debounce(request, 700)} />
    </div>
  );
};

butanti-shakeMeasures do not completely eliminate data being overwritten. If the last request was really slow, then overwriting of subsequent requests can also occur.

3. Cancellation of previous request

When a new request needs to be initiated and the last request is still pending, the last request can be canceled.

const App = () => {
  const cancelSouceRef = useRef(null);

  const request = async () => {
    if () {
      // If there is a previous request pending,then manually cancel the
      ("Manually cancel the last request");
    }
    const source = ();
     = source;

    try {
      const response = await ("/api/data", {
        cancelToken: ,
      });
      setData();
    } catch (error) {
      if ((error)) {
        ("Request canceled", );
      } else {
        ("Request Error", );
      }
    } finally {
       = null;
    }
  };

  return (
    <div className="app">
      <button onClick={request}>requesting</button>
    </div>
  );
};

If the server has already received the request, regardless of whether the front-end has canceled the request or not, the server will complete the query and return it, it's just not processed by the browser anymore.

4. Timing control

We give the request a unique identifier at each request and keep the latest identifier in the component's global, and do not process the result of the response if the identifier at the time of the response indicates the latest identifier.

Markers are fine as long as they are unique to the current component, such as self-incrementing numbers, timestamps, random numbers, and so on.

const App = () => {
  const requestIdRef = useRef(0); // Save the latest requestid

  const request = async () => {
    ++; // Self-adding each time

    const curRequestId = ;
    try {
      const response = await ("/api/data", {
        cancelToken: ,
      });
      if (curRequestId < ) {
        // The current request is not up-to-date,leave sth. untouched
        return;
      }
      setData();
    } catch (error) {
      (error);
    }
  };

  return (
    <div className="app">
      <button onClick={request}>requesting</button>
    </div>
  );
};

This is a relatively simple and effective solution, while allowing users to search at will.

5. Summary

Of course, in practice, there must be a combination of multiple scenarios as well. For example, in a scenario where a pure input box triggers a search, it's typicallyAnti-shake + timing controlThe combination of the two schemes reduces the number of triggered requests and avoids mutual data overwriting.

Some students may think of "controlling the number of concurrent requests", using theformationrecursive (calculation)etc., each time the initiated request is put to the back of the queue and then the request is initiated in the order of the queue. As we've seen before in the articlePromise Asynchronous Concurrency Control in JavaScript This scenario has been explored.

This approach can also solve the problem, but there is a feeling of "killing the chicken with a bull's-eye". Because in today's scenario, when we make multiple requests to the same interface, we actually care more about the result of the latest request, and we can just throw away the result of the previous request.

Welcome to my public number: front-end small tea house.

前端小