0

Looping over two different arrays, both for-loops nested within each other asynchronously (as chunks and setTimeout sourced from here), and trying to use the progress bar example from W3Schools here (the Label one).

The sourced (slightly modified for a 'done' callback) asynchronous function:

function loopAsync(array, fn, done, chunk, context) {
    context = context || window;
    chunk = chunk || 10;
    var index = 0;

    function doChunk() {
        var cnt = chunk;
        while (cnt-- && index < array.length) {
            // callback called with args (value, index, array)
            fn.call(context, array[index], index, array);
            ++index;
            //console.log("index is " + index);
            progressBar(index);
        }
        if (index < array.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        } else {
            done && done();
        }
    }
    doChunk();
}

Regardless of being asnychronous, these are the same problems even with a normal for-loop:

  1. The W3School example is using setInterval, which is inaccurate since the for-loops may already finish processing before setInterval is.

  2. There are two nested for-loops, so instead of tracking the progress of (for example) i in for (var i=0...), it needs to track the first loop * the second loop for accuracy (as to not appear stopped - especially because the second loop will likely have a larger array length than the first).

For example:

Asynchronously using the linked example above:

loopAsync(arr1, function (item1) {
    loopAsync(arr2, function (item2) {
        //Comparing or processing item1 with item2
    });
}, doNext);

Or, basically the same without asynchronous looping:

for (var item1 = 0; item1 < arr1.length; ++item1) {
    for (var item2 = 0; item2 < arr2.length; ++item2) {
        //Doing things... need to track progress of both?
    }
}
  1. Needs to be generic, able to be used in any nested (or non-nested) for-loop operation.

How should these problems be addressed, preferably without jquery?

user1679669
  • 342
  • 5
  • 17
  • But...the code you actually show isn't at all asynchronous. If you want a progress bar you need to rewrite the loop using `setTimeout()` or `setInterval()` instead of `for`, as per the code you linked to. – nnnnnn Feb 20 '17 at 02:35
  • @nnnnnn Please re-read the first paragraph... – user1679669 Feb 20 '17 at 02:37
  • 1
    I read it twice before commenting. You seem to be saying "here is some code that processes an array asynchronously, but I'm going to ignore that and now here is a plain synchronous nested for loop. Why aren't you using the concepts from the code you linked to? Regarding the progress bar code, you don't want a separate interval for that, you'd control it from the same function that does the looping. Do you want a single progress bar for overall progress, or one per loop? – nnnnnn Feb 20 '17 at 02:39
  • _"Regardless of being asnychronous"_ , _"need to track progress of both?"_ What are you tracking the progress of? – guest271314 Feb 20 '17 at 02:45
  • @nnnnnn Because the problem persists regardless of loop type, as I had written. If you so desire, the for-loop using the linked example has now been included. Ideally, whichever is more accurate (updates more) in showing progress of both loops at once? Please refer to the second comment for more information below: – user1679669 Feb 20 '17 at 02:50
  • @guest271314 The second for-loop is using a larger length than the first, and is comparing items (or values) between both. For example, arr1 may be on arr1[0] but iterate over arr2 which may have a length of 5,000 items (compare arr1[0] against every item in arr2). – user1679669 Feb 20 '17 at 02:50
  • _"For example, arr1 may be on arr1[0] but iterate over arr2 which may have a length of 5,000 items (compare arr1[0] against every item in arr2)"_ How is description related to `` element, or progress of procedure? – guest271314 Feb 20 '17 at 02:55
  • @guest271314 I need to know when the for-loops have completed looping (which is determined by arr1.length, looping over each arr2.length item). It would probably be easier tracking only the progress of the first loop than both, but the second array may be longer, thus taking the processing time. – user1679669 Feb 20 '17 at 02:59
  • Requirement is not clear. If there are a finite number of elements within both arrays you can track both or either of `item1` and `item2`. What issue are you having with the current `javascript` implementation that you are using? See http://stackoverflow.com/help/mcve – guest271314 Feb 20 '17 at 03:01
  • @guest271314 The asyncLoop function is being called multiple times, with a local function doing the processing. There is no unique tracking of array length that is possible (or how many items there currently are to track/combine) due to it being locally defined in that function. Furthermore, even if I knew both array lengths combined, I wouldn't know which is the current array being processed. For example, the nested (or child) array could be processing from 0-100%, but it shouldn't be because the parent element still needs to process the next item. – user1679669 Feb 20 '17 at 03:09
  • @user1679669 That does not clarify what issue you are having with `javascript` that you are currently using. Still not sure what measurement you are tracking for progress, or what requirement is. See http://stackoverflow.com/help/mcve – guest271314 Feb 20 '17 at 03:21

1 Answers1

1

i think thats just need basic increment. you can use something like this:

function progress(total) {
  this.inc = (total !== 0? this.inc || 0: -1) + 1;
  var percentage = Math.floor(this.inc / total * 100);
  document.write('Processing ' + this.inc + '/' + total + ' ' + percentage + '%<br/>');
}

var arr1 = [1,2,3,4,5];
var arr2 = [1,2,3,4,5,6,7,8,9];

for (var item1 = 0; item1 < arr1.length; ++item1) {
  for (var item2 = 0; item2 < arr2.length; ++item2) {
      progress(arr1.length * arr2.length);
  }
}

// reseting counter
progress(0);

// count another progress
var arr3 = [1,2,3];

for (var item1 = 0; item1 < arr1.length; ++item1) {
  for (var item2 = 0; item2 < arr2.length; ++item2) {
    for (var item3 = 0; item3 < arr3.length; ++item3) {
        progress(arr1.length * arr2.length * arr3.length);
    }
  }
}

another example with random execution time (use promise to doing async process)

function progress(total) {
  this.inc = (total !== 0? this.inc || 0: -1) + 1;
  document.getElementById('progress').innerHTML = 'Processing ' + Math.floor(this.inc / total * 100) + '%';
}

function processing_something(callback) {
  setTimeout(function(){
    callback();
    //execution between 1 to 10 secs
  }, Math.floor(Math.random() * 10) * 1000);
}

var arr1 = [1,2,3,4,5];
var arr2 = [1,2,3,4,5,6,7,8,9];

for (var item1 = 0; item1 < arr1.length; ++item1) {
  for (var item2 = 0; item2 < arr2.length; ++item2) {
      new Promise(function(resolve) {
       //do something that require time
        processing_something(resolve);
      }).then(function(){
        progress(arr1.length * arr2.length);
      });
  }
}
<div id="progress"></div>
Fal
  • 135
  • 6
  • This might just be the way to go, thanks! Do you know how to constrain `this.inc = (total !== 0? this.inc || 0: -1) + 1;` up to 100% based on the combined total number, instead of outputting only the combined total? – user1679669 Feb 20 '17 at 03:44
  • I believe this will run too fast for the UI to properly show a progress bar... not sure if that's a requirement for you. – l3utterfly Feb 20 '17 at 03:46
  • @l3utterfly i think he can put that progress as a callback on his function inside the loops – Fal Feb 20 '17 at 04:20
  • 2
    Have you tested this? Normally the browser will not repaint the screen while JS is executing, so the progress won't be visible to the user until after all the synchronous JS finishes. That's one of the reasons for converting a conventional loop to use `setTimeout` instead. – nnnnnn Feb 20 '17 at 04:53
  • agree with @nnnnnn here, hence why I said this loop runs too fast for the UI – l3utterfly Feb 20 '17 at 04:55
  • @nnnnnn yeah, thats wont write directly because browser didnt repaint. buts thats just the logic to show the progress. if we combine the logic to async process, we can use promise (example: https://jsfiddle.net/dhr5ew8g) – Fal Feb 20 '17 at 05:28
  • @Fal Thanks for the answer. Two more concerns, though: `progress(0)` seems to report NaN% in your second solution, instead of 0%. Also, would it be possible to expand the `this.inc =` ternary conditional as a common if-else conditional statement? – user1679669 Feb 20 '17 at 16:47
  • @Fal It also seems that after `progress` is called, and the for-loop is completed, that when the for-loop is looped again with a different `arr1.length` (without a `progress(0)` or without a reset), the progress counter will increase without rounding correctly to the nearest 100%. – user1679669 Feb 20 '17 at 16:57
  • 1
    @user1679669 yeah, my answer just an example about how to put a progress on a loop. you can improve the function though. like adding auto reset in the end of progress code with `if (total == this.inc) this.inc = 0` – Fal Feb 21 '17 at 08:41