问题
Say i have a function like :
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);
}
}
Now what i want from this function is that when all of the conditions have performed their intended task, only then it should move to the next iteration of FOR loop. In other words, i=0
in FOR LOOP should change to i=1
and so-on iff all of the conditions inside the FOR loop have completed their job in current iteration.
Currently behaviour of this code is very random (because of setTimeout
i believe). How can i make this work as per my expectations?
I recently read about promises (don't know much about them) but i'm not sure how to implement them in this code or if they will work in this case...
回答1:
The original idea came from the answer of Nina Scholz on some similar question. this answer is good if you don't like promises and deferred object. Othewise Kris Kowal's Q library would be a better choice.
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();
})();
Code Explanation
Iterator class has one queue which is and array and some methods:
next
when you call next()
if there is any callback in queue it will fetch first and execute that. when you call Array#shift it removes and returns first item from array. When this item is a function then you can invoke it by adding parentheses in front of it. here is the expanded version:
if (iterator.queue.length > 0) {
var callback = iterator.queue.shift();
callback();
}
wait
This method will add an extra callback to queue which after a timeout calls the next method. this one is for creating the expected delay.
add This method calls the wait with desired delay then attaches another function to queue which will call the callback and then calls next to make callback chain running.
fillNextIteration
after explaining the iterator there is another loop here on function fillNextIteration
this starts with the conditions for the first iteration for example:
if (someConditionIsTrue) {
iterator.add(function() {
doWhatShallBeDone;
}, delay);
}
you can check all conditions and then if required add the callback as shown.
after all condition finished add fillNextIteration
as last action to continue the loop.
self invoking function
As you can see fillNextIteration
is self invoked.
This helps to reduce one invoke and works like this:
function f(){};
f();
Last thing to mention you can create a loop by calling a function inside itself. if there is no delay or stop for revoke then it would be a deadlock. So this is another loop as well:
(function loop() { setTimeout(loop, delay); })();
回答2:
Try this please :
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);
回答3:
Hope this will work from promise chaning
callSomeFunction1().then(function() {
callSomeFunction2();
}).then(function() {
callSomeFunction3();
})
来源:https://stackoverflow.com/questions/38159088/complete-all-functions-inside-a-for-loop-before-iterating-to-next-iteration-of-t