在迭代到下一个循环之前,完成FOR循环内的所有函数

4
假设我有一个函数如下:
var bigArray = [1,2,3,4,5.........n];
for(var i=0; i<bigArray.length; i++){
  if(someCondition){
     setTimeout(function(){
       ..do_stuff..
       callSomeFunction();
     }, someDelayTime);
  }

  if(someCondition){
     setTimeout(function(){
       ..do_stuff..
       callSomeFunction();
     }, someDelayTime);
  }

  if(someCondition){
     setTimeout(function(){
       ..do_stuff..
       callSomeFunction();
     }, someDelayTime);
  }
  .
  .
  .
  .
  .
  .
  .
  .
  if(someCondition){
     setTimeout(function(){
       ..do_stuff..
       callSomeFunction();
     }, someDelayTime);
  }
}

现在我想让这个函数在所有条件都执行其预期的任务后,才能移动到FOR循环的下一个迭代。换句话说,在FOR循环中的i = 0应该更改为i = 1等等,只有当FOR循环中的所有条件在当前迭代中完成了它们的工作时才会发生。

目前,这段代码的行为非常随机(因为我认为是由于setTimeout)。如何使此代码按照我的期望工作?

我最近读到了promise(不太了解它们),但我不确定如何在此代码中实现它们,或者它们是否适用于此情况......

3
你不能像那样混合同步循环和异步行为。你可能想做的是将承诺映射到数组并对它们进行排序。 - elclanrs
抱歉,由于我对 Promise 的理解很少,我无法解释您提出的解决方案。不过还是谢谢您的快速回复! - Tushar Shukla
@TusharShukla 我认为也应该给原始想法点赞。 - Morteza Tourani
@mortezaT 我相信你正在谈论“已接受的答案”。我很感激大家的帮助,也对他们心存感激,但是按照我的代码要求,我仍然没有解决这个问题。当然,一旦我找到解决方案,我会接受一个答案。 - Tushar Shukla
@TusharShukla 那问题在哪里? - Morteza Tourani
3个回答

2

请尝试以下操作:

bigArray = [1, 2, 3, 4, 5.........n];
neededStuff = 10; // Number of finished works you need below
stuffOK = neededStuff;
function mainFunction(i) {

    function loopFunction(i) {
        if (someCondition) {
            setTimeout(function () {
                // .. do_stuff..
                stuffOK++;
                callSomeFunction();
            }, someDelayTime);
        }

        if (someCondition) {
            setTimeout(function () {
                // .. do_stuff..
                stuffOK++;
                callSomeFunction();
            }, someDelayTime);
        }

        if (someCondition) {
            setTimeout(function () {
                // .. do_stuff..
                stuffOK++;
                callSomeFunction();
            }, someDelayTime);
        }
        /*

         ...

         */

        if(someCondition) {
            setTimeout(function () {
                // .. do_stuff..
                stuffOK++;
                callSomeFunction();
            }, someDelayTime);
        }
    }

    if (stuffOK == neededStuff) {
        i++;
        stuffOK = 0; // reinit counter
        loopFunction(i);
    }

    setTimeout('mainFunction(' + i + ');', 100);

}
mainFunction(-1);

2

这个想法来自Nina Scholz在一些类似问题上的答案。 如果您不喜欢承诺和延迟对象,那么这个答案是很好的选择。否则,Kris KowalQ库会是一个更好的选择。

function Iterator() {
  var iterator = this;
  this.queue = [];

  this.add = function(callback, wait) {
    iterator.queue.push(iterator.wait(wait));

    iterator.queue.push(function() {
      callback();
      iterator.next();
    });
  };

  this.wait = function(wait) {
    return function() {
      setTimeout(iterator.next, wait);
    };
  };

  this.next = function() {
    iterator.queue.length && iterator.queue.shift()();
  };
}

var arr = [1, 2, 3, 4, 5],
  counter = -1;

var iterator = new Iterator();

(function fillNextIteration() {
  if (counter >= arr.length)
    return;
  counter++;
  iterator.add(function() {
    console.log('1 - counter value is ' + counter);
  }, 100);
  iterator.add(function() {
    console.log('2 - counter value is ' + counter);
  }, 100);
  iterator.add(function() {
    console.log('3 - counter value is ' + counter);
  }, 100);
  iterator.add(function() {
    console.log('4 - counter value is ' + counter);
  }, 100);

  iterator.add(fillNextIteration, 100);
  iterator.next();

})();

代码解释

迭代器类有一个队列,它是一个数组,并且有一些方法:

next

当你调用 next() 方法时,如果队列中有任何回调函数,它将获取第一个回调并执行。当你调用 Array#shift 方法时,它会从数组中移除并返回第一个项。当这个项是一个函数时,你可以在它前面添加括号来调用它。以下是展开的版本:

if (iterator.queue.length > 0) {
    var callback = iterator.queue.shift();
    callback();
}

等待

此方法将在队列中添加一个额外的回调函数,该函数在超时后调用下一个方法。这是为了创建预期的延迟。

添加 此方法使用所需的延迟调用等待,然后将另一个函数附加到队列中,该函数将调用回调函数,然后调用下一个函数以使回调链运行。


填充下一个迭代 在解释迭代器之后,此处还有另一个循环在函数fillNextIteration中开始,例如第一次迭代的条件:

if (someConditionIsTrue) {
    iterator.add(function() {
        doWhatShallBeDone;
    }, delay);
}

您可以检查所有条件,然后如下所示添加回调函数。 在所有条件完成后,将fillNextIteration添加为最后一个操作以继续循环。

自执行函数 正如您所看到的,fillNextIteration是自动调用的。 这有助于减少一个调用,并像这样工作:

function f(){};
f();

最后需要提到的是,你可以通过在函数内部调用自身来创建一个循环。如果没有延迟或停止来撤销操作,那么就会出现死锁。因此,这也是一个循环:

(function loop() { setTimeout(loop, delay); })();

你能否解释一下这段代码,我对这种风格的js代码不是很熟悉。(无意冒犯)。 - Tushar Shukla
@TusharShukla 添加了一些注释。希望能有所帮助。 - Morteza Tourani
谢谢您的解释,现在我可以将其用于我想要的目的了。非常感谢! - Tushar Shukla

-1
希望这个能够在 Promise 链中正常工作。
callSomeFunction1().then(function() {
   callSomeFunction2();
}).then(function() {
   callSomeFunction3();
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接