Javascript - do something every x number of iterations in loop - javascript

I'm using the Google Maps API which has a limit of 10 requests per second. I want to have a short delay every 10 requests.
for (var i = 0; i < myRequests.length; i++) {
// pause every 10 iterations
}
My maths aren't sharp... How can I know whenever I reach 10 iterations in order to do something?

(function loop(z) {
for (var i = 0; i < 10; i++) {
//use myRequests[z]
console.log(myRequests[z]);
z++;
if(z===myRequests.length)
return;
}
setTimeout(function(){loop(z)},1000);
})(0);
DEMO

Create an array that stores the requests and use this:
var index = -1;
for(var i=0; i<anArray.length; i++){
if(anArray[i]!=false){
index = i;
break;
}
}
if(index!=-1){
function makeCall(){
//api calls
anArray[index] = true;
}
}else{
setTimeout(makeCall, 1000)
}

Loops can't wait
The only way to "wait" is to use a timeout, unfortunately for this situation timeouts are asynchronous, so they are useless in a loop because the loop will continue on anyway.
Instead, you will need to use a recursive function.
The math part
We can save a bunch of logic by using the modulo operator (%) to get the remainder of the currently incremented count divided by the split that you want to wait at. If the remainder is 0 (the current count is a multiple of the split), wait the requested amount of time.
Example
function log(out) { document.body.innerHTML += out; }
function repeat(count, split, limit, wait) {
var args = arguments;
if(++count <= limit) {
log([count, '0' + count][+(count < 10)] + ', ');
if(!(count % split) && count < limit) {
log('wait<br>');
setTimeout(function(){
repeat.apply(null, args);
}, wait);
} else repeat.apply(null, args);
} else log('done');
}
repeat(0, 10, 50, 1000);

You can use the modulus to determine, if you're in the 10th iteration.
for (var i = 0; i < myRequests.length; i++) {
// pause every 10 iterations
if (i%10==0){
sleep(5000)
}
}

Related

How to pause a loop after x iterations for x seconds then resume

So I have an array that has a list of Channel Names that I would like to join (This is for a Twitch Chat Bot), the API endpoint for joining a channel has a rate limit of 50 joins per 15 seconds. I am trying to figure out the best way to iterate through 50 channel names, pause for 15 seconds, then resume iterating through the rest of the array, pausing for 15 seconds every 50 names.
I had originally tried a generic for loop using a fake, 100 value array, a Modulus Operator, and setTimeout, but to no avail. But in all honesty, I didn't know where to start so it is quite bad.
let array = ([...Array(100).keys()].map(x => ++x))
for (var i = 1; i < array.length; i++) {
if (i % 50 === 0) {
setTimeout(() => {
console.log('Waiting 15 seconds.')
}, 15000);
} else {
console.log(array[i])
}
}
Ideally, it would log 1-50, then wait 15 seconds, then log 51-100.
You can use async and await to pause your for loop in a fairly simple fashion:
// utility function that returns a promise that resolves after t milliseconds
function delay(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
async function processArray(array) {
for (let i = 1; i < array.length; i++) {
if (i % 50 === 0) {
await delay(15 * 1000);
}
console.log(array[i])
}
}
let data = ([...Array(100).keys()].map(x => ++x))
processArray(data).then(() => {
console.log("all done");
});
FYI, I don't quite understand why you're trying to use index 1 through 100 on a 100 element array. I think you should be using indexes 0 through 99 for a 100 element array. I left the code that way you had in under the assumption that maybe you're doing this on purpose.
const _ = require('lodash');
Use iterators/generators so that you can control when you want the next item instead of fighting to "stop" the execution of the loop.
function* myIterator(data) {
for (const item of data)
yield item;
}
Then set up a function that will do the actual execution, taking the iterator as a parameter. This way, each time you call it, you can pass in the iterator so that it remembers where it left off.
function execute(it) {
// We want to do this in batches of 50
// (but test with lower value to better see how it works)
_.range(0, 50).forEach(() => {
// Get next item
const item = it.next();
// If no more items, we're done
if (item.done) return;
else {
// Do something with the item
console.log(item.value);
};
});
// Pause for 15 seconds and then continue execution
setTimeout(() => execute(it), 15000);
}
Create your data, generate an iterator from it and then execute.
(function main() {
const data = _.range(1, 101);
const it = myIterator(data);
execute(it);
})();
Try this
let array = ([...Array(100).keys()].map(x => ++x))
const startLoop = currentIndex => {
for (let i = currentIndex; i < array.length; i++) {
if (i % 50 === 0) {
setTimeout(() => {
console.log('Waiting 15 seconds.');
startLoop(i + 1);
}, 15000)
break;
}
console.log(array[i])
}
}
startLoop(1);
Writing a recursive loop function from scratch (at the cost of performance) is probably the simplest solution, but you can accomplish this iteratively using a while loop and a Promise, without compromising performance.
In the code below, every time the 1-based index of the loop reaches a multiple of batch_size, an await is called which stops execution until the Promise resolves. The promise is just a setTimeout call, which waits for pause_ms before allowing the loop to continue. The values are slightly different here just to make testing easier; you can change them freely to meet your needs.
const vals = [...new Array(20)].map(() => Math.floor(Math.random() * 9) + 1);
console.log(vals);
async function iterateBatches(arr, batch_size, pause_ms) {
// Create a promise generator that will automatically resolve after the desired number of millseconds.
const wait = () => new Promise(r => {
setTimeout(r, pause_ms)
});
// Establish the starting and ending points for the iteration.
const len = arr.length;
let i = 0;
// As long as the loop hasn't reached the final element,
while (i < len) {
// Perform an operation with the value in your array.
console.log(i, vals[i]);
// If we've reached the end of the array, break out of the loop to prevent an unneeded iteration.
if (i >= len - 1) break;
// Increment the index (this is also equal to the 1-based index, which saves us some lines).
// If the 1-based index is a multiple of batch_size and we're not on the first iteration, wait for our promise generator.
if (++i % batch_size === 0 && i > 0) await wait();
}
console.log("Done!");
}
iterateBatches(vals, 5, 2000);
You could just create the loop manually. Here's a simplified example...
var array = ['a','b','c','d','e','f'];
var i = 0;
function loop(){
if(i>=array.length) return;
if(i==3){
setTimeout(function(){
i++;
loop();
},15000);
return;
}
i++;
console.log(array[i]);
loop();
}
loop();

JS crashes sometimes with Timer scramble

My Javascript timer is for people with a rubiks cube with generates a scramble (nevermind all this, but just to tell you I'm generating after each solve a new scramble will be generated) and my scrambles do actually have a while (true) statement. So that does crash my script, but it 95/100 times stops just before the script crashes but I don't wanna have any times.
Let me explain a bit more detailed about the problem.
Problem: javascript crashes because my script takes too long to generate a scramble.
Below you have 3 functions I use.
This function generates a scramble with the Fisher-Yates shuffle.
Timer.prototype.generateScramble = function(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
This function validates the input e.g. I receive an array as the following:
Here I only have to check the first character. That's why I use the seconds [ ] notation. I don't want people get an F with an F2 e.g.
var scr = ["F","R","U","B","L","D","F2","R2","U2","B2","L2","D2","F'","R'","U'","B'","L'","D'"]
Timer.prototype.validateScramble2 = function(array) {
var last = array.length-1;
for (var i = 0; i < array.length-1; i++) {
if (array[i][0] == array[i+1][0]) {
return false;
}
}
for (var i = 0; i < array.length-2; i++) {
if (array[i][0] == array[i+2][0]) {
return false;
}
}
if (array[0][0] == [last][0]) {
return false;
}
return true;
};
The above functions are just waiting to be called. Well in the function below I use them.
Timer.prototype.updateScramble2 = function(scrambleArr, len, type) {
var self = this;
var scramble = '', j, updatedArr = [];
while (updatedArr.length < len) {
j = (Math.floor(Math.random() * scrambleArr.length));
updatedArr.push(scrambleArr[j]);
}
while (!self.validateScramble2(updatedArr)) {
updatedArr = self.generateScramble(updatedArr);
}
for (var i = 0; i < updatedArr.length; i++) {
scramble += updatedArr[i] + ' ';
}
scrambleDiv.innerHTML = scramble;
};
I assume you guys understand it but let me explain it briefly.
The first while-loop adds a random value from the given array(scrambleArr) into a new array called updatedArr.
The next while-loop calls the validateScramble2() function if there isn't in an array F next to an F2.
The for-loop adds them into a new variable added with a whitespace and then later we show the scramble in the div: scrambleDiv.innerHTML = scramble;
What do I need know after all this information?
Well I wanna know why my updateScramble2() functions lets my browser crash every time and what I do wrong and how I should do it.
I'm not entirely sure I understand the question, but from the way your code looks, I think you have an infinite loop going on. It appears as if validateScramble2 always returns false which causes your second loop in updateScramble2 to perpetually run.
I suggest you insert a breakpoint in your code and inspect the values. You could also insert debugger; statements in your code, works the same way. Open dev tools prior to doing these.
A suggestion is instead of using loops, use a timer. This breaks up your loop into asynchronous iterations rather than synchronous. This allows the browser breathing space for other operations. Here's an example of a forEachAsync:
function forEachAsync(array, callback){
var i = 0;
var timer = setInterval(function(){
callback.call(null, array[i]);
if(++i >= array.length) clearInterval(timer);
}, 0);
}
forEachAsync([1,2,4], function(item){
console.log(item);
});
You can take this further and use Promises instead of callbacks.

setTimeout with Loop in JavaScript [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 1 year ago.
I have a very trivial question. For a simple loop with setTimeout, like this:
for (var count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + count);
}, 1000 * count);
}
console gives an output like this:
Count = 3
Count = 3
Count = 3
Not sure why the output like this. Anyone could explain, please?
This has to do with how scoping and hoisting is being treated in JavaScript.
What happens in your code is that the JS engine modifies your code to this:
var count;
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + count);
}, 1000 * count);
}
And when setTimeout() is being run it will first look in it's own scope after count but it won't find it so then it'll start looking in the functions that closes (this is called closures) over the setTimeout function until it finds the var count statement, which will have the value 3 since loop will have finished before the first timeout function has been executed.
More code-ily explained your code actually looks like this:
//first iteration
var count = 0; //this is 1 because of count++ in your for loop.
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + 1);
}, 1000 * 1);
}
count = count + 1; //count = 1
//second iteration
var count = 1;
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + 2);
}, 1000 * 2);
}
count = count + 1; //count = 2
//third iteration
var count = 2;
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + 3);
}, 1000 * 3);
}
count = count + 1; //count = 3
//after 1000 ms
window.setTimeout(alert(count));
//after 2000 ms
window.setTimeout(alert(count));
//after 3000 ms
window.setTimeout(alert(count));
think about it like that:
AFTER the 1000*n miliseconds are over, what will be the value of count?
of course it will be 3, because the foor loop ended way earlier than the timeout of 1000*n ms.
in order to print 1,2,3 you'll need the following:
for (var count = 0; count < 3; count++) {
do_alert(num);
}
function do_alert(num) {
setTimeout(function() {
alert("Count = " + num);
}, 1000 * num);
}
a different approach is to make it a closure function (explained well in JavaScript closures vs. anonymous functions)
for (var count = 0; count < 3; count++) {
(function(num){setTimeout(function() {
alert("Count = " + num);
}, 1000 * num)})(count);
}
these two code samples will actually work similarly.
the first sample calls a named function (do_alert) each iteration.
the second sample calls a CLOSURE anonymous function (which is just like do_alert) each iteration.
it's all a matter of SCOPE.
hope that helps.
This is to do with closure scoping. The same variable count is available in the scope for each of the setTimeout callback functions. You are incrementing its value and creating a function, but each instance of the function has the same variable count in its scope, and by the time the callback functions execute it will have the value 3.
You need to create a copy of the variable (e.g. var localCount = count) inside a new scope in the for loop to make this work. As for doesn't create a scope (which is the cause of the whole thing) you need to introduce one with a function scope.
e.g.
for (var i = 0; i < 5; i++) {
(function() {
var j = i;
setTimeout(function() {
console.log(j)
},
j*100);
})();
}
Easy fix here is to utilize es6 let local variable. Your code will look almost the same except it will do what you expect :)
for (let count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + count);
}, 1000 * count);
}
Or you could create a recursive function to get that job done, as following:
function timedAlert(n) {
if (n < 3) {
setTimeout(function() {
alert("Count = " + n);
timedAlert(++n);
}, 1000);
}
}
timedAlert(0);
Think about it:
The code executes a loop, in that loop it sets some code to run later.
The loop finishes.
The setTimeout code executes. What's the value of count going to be? The loop finished ages ago...
First, setTimeout(function, milliseconds) is a function which takes a function to execute after "milliseconds" milliseconds.
Remember, JS treats functions as objects, so the for(...) loop will initially produce something like:
setTimeout( ... )
setTimeout( ... )
setTimeout( ... )
Now the setTimeout() functions will execute one by one.
The setTimeout() function will try to find the count variable in the current scope. Failing that, it will go to the outer scope and will find count, whose value is already incremented to 3 by the for loop.
Now, starting execution....The first alert shows immediately, as the milliseconds is 0, the second alert shows after 1000 ms, and then the third alert shows after 2000 ms. All of them shows Count = 3
That's because all the timeouts are run when the loop finished.
The timeout functions then take the current value of count.
And thats always 3 because the for loop has finished.
That is because by the time the for loop completes its execution the count is 3, and then the set timeout is called.
Try this:
var count = 0;
setTimeout(function() {
for (count = 0; count < 3; count++) {
alert("Count = " + count);
}
}, 1000* count);
Better solution IS "Forget both Loops and Recursion" in this case and use this combination of "setInterval" includes "setTimeOut"s:
function iAsk(lvl){
var i=0;
var intr =setInterval(function(){ // start the loop
i++; // increment it
if(i>lvl){ // check if the end round reached.
clearInterval(intr);
return;
}
setTimeout(function(){
$(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
},50);
setTimeout(function(){
// do another bla bla bla after 100 millisecond.
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
$("#d"+seq[i-1]).prop("src",pGif);
var d =document.getElementById('aud');
d.play();
},100);
setTimeout(function(){
// keep adding bla bla bla till you done :)
$("#d"+seq[i-1]).prop("src",pPng);
},900);
},1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
}
PS: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.
PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..

Create a JavaScript loop without while

I am trying to create a page which needs to preform lots of loops. Using a while/for loops cause the page to hang until the loop completes and it is possible in this case that the loop could be running for hours. I have also tried using setTimeout, but that hits a recursion limit. How do I prevent the page from reaching a recursion limit?
var looper = {
characters: 'abcdefghijklmnopqrstuvwxyz',
current: [0],
target: '',
max: 25,
setHash: function(hash) {
this.target = hash;
this.max = this.characters.length;
},
getString: function() {
string = '';
for (letter in this.current) {
string += this.characters[this.current[letter]];
}
return string;
},
hash: function() {
return Sha1.hash(this.getString());
},
increment: function() {
this.current[0] += 1;
if (this.current[0] > this.max) {
if (this.current.length == 1) {
this.current = [0, 0];
} else {
this.current[1] += 1;
this.current[0] = 0;
}
}
if (this.current[1] > this.max) {
if (this.current.length == 2) {
this.current[2] == 0;
} else {
this.current[3] += 1;
this.current[2] = 0;
}
}
},
loop: function() {
if (this.hash() == this.target) {
alert(this.getString());
} else {
this.increment();
setTimeout(this.loop(), 1);
}
}
}
setInterval is the usual way, but you could also try web workers, which would be a more straightforward refactoring of your code than setInterval but would only work on HTML5 browsers.
http://dev.w3.org/html5/workers/
Your setTimeout is not doing what you think it's doing. Here's what it's doing:
It encounters this statement:
setTimeout(this.loop(), 1);
It evaluates the first argument to setTimeout, this.loop(). It calls loop right there; it does not wait for a millisecond as you likely expected.
It calls setTimeout like this:
setTimeout(undefined, 1);
In theory, anyway. In reality, the second step never completes; it recurses indefinitely. What you need to do is pass a reference to the function rather than the returned value of the function:
setTimeout(this.loop, 1);
However, then this will be window on the next loop, not looper. Bind it, instead:
setTimeout(this.loop.bind(this), 1);
setInterval might work. It calls a function every certain amount of milliseconds.
For Example
myInterval = setInterval(myFunction,5000);
That will call your function (myFunction) every 5 seconds.
why not have a loop checker using setInterval?
var loopWorking = false;
function looper(){
loopWorking = true;
//Do stuff
loopWorking = false;
}
function checkLooper()
{
if(loopWorking == false)
looper();
}
setInterval(checkLooper, 100); //every 100ms or lower. Can reduce down to 1ms
If you want to avoid recursion then don't call this.loop() from inside of this.loop(). Instead use window.setInterval() to call the loop repeatedly.
I had to hand-code continuation passing style in google-code prettify.
Basically I turned
for (var i = 0, n = arr.length; i < n; ++i) {
processItem(i);
}
done();
into
var i = 0, n = arr.length;
function work() {
var t0 = +new Date;
while (i < n) {
processItem(i);
++i;
if (new Date - t0 > 100) {
setTimeout(work, 250);
return;
}
}
done();
}
work();
which doesn't hit any recursion limit since there are no recursive function calls.

setInterval on a for loop?

for (var i=list.length; i>=0; i--){
//do something....
}
I want to use a setInterval to make this process take 1 whole min no matter how many items are in the list. So if there are 10 items it would trigger every 6sec, 30items, every two seconds, etc.
Thanks for any help!
Something like this could be done:
var list = [1,2,3,4,5,6,7,8,9,10];
var timeFrame = 60000;
var interval = timeFrame / (list.length-1);
var i = 0;
(function iterate () {
if (list.length > i) {
console.log(list[i]);
i++;
}
setTimeout(iterate, interval);
})();
JsFiddle Demo
I am not sure if this is what you're looking for, but this will "iterate" through all items in the list, without using for loop, in the given timeframe. The function will always "call itself" using setTimeout. The timeout is calculated in the beginning based on the number of items.
This solution is much more "trustable" than setInterval. The next timeout will be set when the previous action is already done, so it won't stack up.
var totalItem = list.length;
setInterval(function(){ alert('');}, 60000/totalItem);
You'd do
function code(i) {
return function() { alert(i); };
}
var period = 60 * 1000 / (list.length - 1);
for (var i=list.length; i>=1; i--){
setTimeout(code(list[i - 1]), period * (i - 1));
}
Try something like the following:
var interval = 2;
for (var i=list.length; i>=0; i--){
setTimeout(your_code_here(), i*interval);
}

Categories