Search code examples
javascriptjqueryloopsasynchronousjavascript-objects

Asynchronously iterate through large array of objects using JS / JQuery


I have a large (~10,000 items) object of objects read from a JSON file and stored as a local variable, in a format something like this:

{
    "some_uuid_1" : {"code":"some_code_1", "name":"asdf"},
    "some_uuid_2" : {"code":"some_code_2", "name":"qwer"},
    ...
    "some_uuid_n" : {"code":"some_code_n", "name":"zxcv"}
}

I'd like to iterate through the entire main object, compare each element's name property to some variable checkName, and append the element's code property to a DOM element if there is a match, as such:

function myFilter(checkName)
{
    var myArray = Object.values(myObj);

    for (var i = 0; i < myArray.length; i++)
    {
        if (myArray[i]["name"] == checkName)
        {
            $("#someElement").append(`${myArray[i]["code"]} <br />`);
        }
    }
}

However, as the size of the object is quite large, I'd like to run the function asynchronously so that it doesn't freeze up the browser while it's running. I don't mind if the DOM element #someElement is slowly populated in the background while other code runs.

How may I accomplish this using JavaScript and/or JQuery?


Solution

  • A small helper might help here:

    function asyncForEach(arr, cb, done) {
      (function next(i) {
        if(i >= arr.length) {
           if(done) done();
           return;
        }
        cb(arr[i], i, arr);
        setTimeout(next, 0, i + 1); // a small trick to defer actions
      })(0);
    }
    

    Or to optimize it you could chunk the results and only yield every 1000 iterations or so:

    function asyncForEach(arr, cb, done) {
      (function next(i) {
        if(i >= arr.length) {
           if(done) done();
           return;
        }
        let stop = i + 1000;
        setTimeout(next, 0, stop); // a small trick to defer actions
        while(i < arr.length && i < stop)
          cb(arr[i], i++, arr);
      })(0);
    }
    

    Which can be used like this in your case:

    asyncForEach(myArray, function(el) {
      if (el.name === checkName){
        $("#someElement").append(`${el.code} <br />`);
      }
    });
    

    However probably the slowest part here is appending to the dom. If you don't want to have "live progress" its probably good to batch the dom update to one single call:

    let result = "";
    asyncForEach(myArray, function(el) {
      if (el.name === checkName){
        result += `${el.code} <br />`;
      }
    }, function() {
      $("#someElement").append(result);
    });
    

    And then even the synchrobous variant might be fast enough:

    let result = "";
    for(const el of myArray) {
      if(el.name === checkName)
        result += `${el.code} <br />`;
    }
    $("#someElement").append(result);