0

I have two buttons for triggering requests, both of which are data requests to the same API, with different buttons representing different parameters.

For example, button A is requesting data for the year 2023 and button B is requesting data for the year 2022:

// Click button A
fetch('same/api', {
  body: {
    year: 2023
  }
}).then(res => {
  store = res;
})

// Click button B
fetch('same/api', {
  body: {
    year: 2022
  }
}).then(res => {
  store = res;
})

The store variable is used to store the response data.

My question is: When I click button A and then click button B in a very short interval, will the response result of button A overwrite the response result of button B? If so, how can I solve such a defect?

I have created a simple demo on codepen: simple demo.

Issue update: Out-of-date responses incorrectly overwrite the correct results, and the question now is how to fix this flaw. Existing solutions:

  1. use a synchronization primitive
  2. use an object or a map to store the results separately with an identifier
  3. discard the request

Does anyone know which method is better? Or is there another way to solve this problem?

lai9fox
  • 59
  • 7
  • 1
    Yes it will, you need to use a synchronization primitive like a Mutex or Lock – mousetail Jan 27 '23 at 07:01
  • 1
    Or disable the buttons while the data is loading, an indication to the user is good anyway – CherryDT Jan 27 '23 at 07:22
  • One more question, does this have anything to do with Microtask, something I'm not familiar with yet. Or is it simply some network, server reasons. @mousetail – lai9fox Jan 27 '23 at 07:29
  • I don't know microtask either – mousetail Jan 27 '23 at 07:37
  • It depends on what you do with `store`. It looks suspicious that `store` is not processed *at the moment* it is set to a value. If you immediately process `store` to do whatever you need it for (for displaying stuff in the right place?) there is no problem. We need more context (code) of what you do with `store`. – trincot Jan 27 '23 at 08:20
  • 1
    So if you have a request firing off when one is active, you should cancel it. https://stackoverflow.com/a/47250621/14104 – epascarello Jan 27 '23 at 13:53

1 Answers1

1

You could check whether the response concerns the latest requested year. If not, ignore the response:

const store = document.querySelector(".display");
const lastFetch = document.querySelector("#lastfetch");

function mockRequest(value) {
    return new Promise((resolve, reject) => {
        const timeCost = Math.random() * 5000;
        setTimeout(() => {
            console.log(`fetch ${value} cost: ${timeCost}`);
            resolve(`Data fetched for year ${value}`);
        }, timeCost);
    });
}

function query(value) {
    if (value) {
        lastFetch.textContent = value;
        mockRequest(value).then((res) => {
            if (value != lastFetch.textContent) {
                console.log(`Got late response for year ${value}: ignored`);
                return;
            }
            store.textContent = res;
        });
    } else {
        lastFetch.textContent = "";
        store.textContent = "no data to display";
    }
}
<div class="display">no data to display</div>
<!--  request trigger buttons  -->
<button onclick="query(2022)">2022</button>
<button onclick="query(2023)">2023</button>
<button onclick="query()">reset</button>
<div>Last Fetch: <span id="lastfetch"></span></div>
trincot
  • 317,000
  • 35
  • 244
  • 286