How to make a javascript FOR LOOP wait for certain

2019-08-06 11:44发布

I have to call up a function (checkImdb) that will fetch some info from a php file (temp.php) and put some contents on a div (placeToFetchTo). This has to be done a certain amount of times, so I used a FOR LOOP for that. The problem is that only the last instance of the looped counter (currentCastId) gets used. I understand there needs to be a way to force the FOR LOOP to wait for the fetch to be complete, and I have been looking online for answers but nothing seems to work so far. I apologise if I have missed an eventual answer that already exists. Any help is appreciated. This is the code I am referring to:

function checkImdb (totalCasts) {
    $(function() {
        for (currentCastId = 1; currentCastId <= totalCasts; currentCastId++) {
                //Gets cast IMDB#
                var row = document.getElementById("area2-" + currentCastId)
                row = row.innerHTML.toString();
                var fetchThis = "temp.php?id=" + row + "\ .filmo-category-section:first b a";
                placeToFetchTo = "#area0-" + currentCastId;

                function load_complete() {
                    var filhos = $(placeToFetchTo).children().length, newDiv ="";
                    var nrMoviesMissing = 0, looped = 0;
                            alert("done- "+ placeToFetchTo);

                }

                document.getElementById("area0").innerHTML = document.getElementById("area0").innerHTML + "<div id=\"area0-" + currentCastId + "\"></div>";

                $(placeToFetchTo).load(fetchThis, null, load_complete);

        } //End of: for (imdbLooper = 0; imdbLooper <= totalCasts; imdbLooper++) {

    }); //End of: $(function() {
}

2条回答
时光不老,我们不散
2楼-- · 2019-08-06 11:45

2017 update: The original answer had the callback arg as last arg in the function signature. However, now that the ES6 spread operator is a real thing, best practice is to put it first, not last, so that the spread operator can be used to capture "everything else".


You don't really want to use a for loop if you need to do any "waiting". Instead, use self-terminating recursion:

/**
 * This is your async function that "does things" like
 * calling a php file on the server through GET/POST and
 * then deals with the data it gets back. After it's done,
 * it calls the function that was passed as "callback" argument.
 */
function doAsynchronousStuff(callback, ...) {
  //... your code goes here ...

  // as final step, on the "next clock tick",
  // call the "callback" function. This makes
  // it a "new" call, giving the JS engine some
  // time to slip in other important operations
  // in its thread. This basically "unblocks"
  // JS execution.
  requestAnimationFrame(function() {
    callback(/* with whatever args it needs */);
  });
}

/**
 * This is your "control" function, responsible
 * for calling your actual worker function as
 * many times as necessary. We give it a number that
 * tells it how many times it should run, and a function
 * handle that tells it what to call when it has done
 * all its iterations.
 */
function runSeveralTimes(fnToCallWhenDone, howManyTimes) {
  // if there are 0 times left to run, we don't run
  // the operation code, but instead call the "We are done"
  // function that was passed as second argument.
  if (howManyTimes === 0) {
   return fnToCallWhenDone();
  }

  // If we haven't returned, then howManyTimes is not
  // zero. Run the real operational code once, and tell
  // to run this control function when its code is done:
  doAsynchronousStuff(function doThisWhenDone() {
    // the "when done with the real code" function simply
    // calls this control function with the "how many times?"
    // value decremented by one. If we had to run 5 times,
    // the next call will tell it to run 4 times, etc.
    runSeveralTimes(fnToCallWhenDone, howManyTimes - 1);
  }, ...);
}

In this code the doAsynchronousStuff function is your actual code.

The use of requestAnimationFrame is to ensure the call doesn't flood the callstack. Since the work is technically independent, we can schedule it to be called "on the next tick" instead.

The call chain is a bit like this:

// let's say we need to run 5 times
runSeveralTimes(5);
=> doAsynchronousStuff()
   => runSeveralTimes(5-1 = 4)
      => this is on a new tick, on a new stack, so
         this actually happens as if a "new" call:
runSeveralTimes(4)
=> doAsynchronousStuff()
   => runSeveralTimes(4-1 = 3), on new stack
runSeveralTimes(3)
...
=> doAsynchronousStuff()
   => runSeveralTimes(1-1 = 0), on new stack
runSeveralTimes(0)
=> fnToCallWhenDone()
=> return
<end of call chain>
查看更多
爷的心禁止访问
3楼-- · 2019-08-06 11:55

You need to use a while loop and have the loop exit only when all your fetches have completed.

function checkImdb (totalCasts) {
 currentCastId = 1;
 totalCasts = 3;
 doneLoading = false;

 while (!doneLoading)
 { 
       //do something
       currentCastId++;

      if (currentCastId == totalCasts)
           doneLoading = true;
 }
}
查看更多
登录 后发表回答