Search code examples
javascriptasynchronousfetch-apiinfinite-scroll

How to only consider the lastest call of an asynchronous function (JS fetch / infinite scroll)


I'm not sure it is called an infinite scroll, but basically, I have a table with several thousands lines which I display in a ASP.Net Core website. The table has an advance layout where the style strongly depend on the cell content. I populate content and the style of the rows via the controller and post a JSON file.

I'm able to display let's say 20 rows of the table thanks to the JS fetch api. I'm able to scroll to the adjaceent rows smothly

I also added a separate scrollbar, so I can jump to any row from the first to the last one.

The problem is that when I stop moving the scrollbar, the rows still update for a few seconds (with the some intermediate rows, but fortunately not all) until the target ones are reached. It seems that there is some delay like if I have several asynchronous call waiting to process the update function which executes one after another.

Is there a way to only considered the latest call to the update function?

        $('#fields-scroll').on('input change', async function () {
            
            var row = $('#fields-scroll').val();
            
            await scroll(row);
        });

        async function scroll(row) {

            // Posts the metrics to the controller.
            const response = await fetch(`${window.location.href}Fields/Interact/`, {
                method: 'POST',
                body: JSON.stringify({ Row: row}),
                headers: {
                    'Content-Type': 'application/json; charset=utf-8'
                }
            });

            // Gets the results from the controller.
            const results = await response.json();
            
            // Makes sure that there are actual results.
            if (results == null) {
                
                return;
            }

            var fields = results.fields;

            await populate(fields);
        }

Thanks for any help!


Solution

  • I think the easiest way ( without using external libs like redux-saga ) would be to use a simple debouncer to debounce the scroll action:

    const handler = _.debounce(async function () {
      console.log('SCROLLING');
      try {
        const row = $('#fields-scroll').val();
        await scroll(row);
      } catch (e) {
        console.log(e);
      }
    }, 200);
    
    $('#fields-scroll').on('input change', handler);
    
    async function scroll(row) {
      // Posts the metrics to the controller.
      const response = await fetch(`${window.location.href}Fields/Interact/`, {
        method: 'POST',
        body: JSON.stringify({ Row: row }),
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
        },
      });
      // Gets the results from the controller.
      const results = await response.json();
      // Makes sure that there are actual results.
      if (results == null) {
        return;
      }
    
      var fields = results.fields;
    
      await populate(fields);
    }
     <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type="range" id="fields-scroll" />

    If this is the desired effect, you can adjust the debounce timing according to your preferences. In any ase remember to always wrap the statements inside an async function inside a try-catch block, or you won't catch thrown errors.