I have a simple setInterval function that is done inside of a for loop. My goal is that I want the function to run on each position in the array, and once it reaches the end, I want it to stop. However, this is not happening. The timeout function is continuing to run infinitely. Can anyone explain where I'm going wrong and what I need to do to fix this?
JS
Keyhole.bufferArray = [Keyhole.twoMileBuffer, Keyhole.fiveMileBuffer, Keyhole.tenMileBuffer, Keyhole.twentyFiveMileBuffer];
var ticker = -1;
for(var i = 0; i < Keyhole.bufferArray.length; i++){
var populateServices = setInterval(function(){
++ticker;
addBuffersToService(Keyhole, i);
if(ticker >= Keyhole.bufferArray.length - 1){
ticker = -1;
clearInterval(populateServices);
}
}, 1000)
}
function addBuffersToService(Keyhole, index){
console.log(Keyhole);
}
Because you have a for loop that is making an interval for every index of the array. You should not be using the for loop if you are looping over the array with the interval. Remove the for loop.
The problem is that you overwrite your interval handler in loop. I suggest you to kepp handlers in array and remove them according to iterator varialble:
var ticker = -1;
var populateServices = [];
for(var i = 0; i < Keyhole.bufferArray.length; i++){
populateServices[ticker + 1] = setInterval(function(){
...
clearInterval(populateServices[ticker + 1]);
ticker = -1;
Note that array identifiers should be positive numbers so you should add +1 inside handlers array.
And don't forget to set ticker to -1 after clearInterval invokation.
Related
In a loop, how to update the state as many times as the loop?
For below, the updateDataset() only update the state when the loop finishes.
Is there a way to update the state along with how many times the looping occur, so that the app re-render each round of looping
const [dataset, updateDataset] = useState([]);
function bubbleSort() {
//how many rounds of comparison
var sortedArray = dataset.slice();
for (var i = sortedArray.length; i > 0; i--) {
//how many comparison pair
for (var j = 0; j < i - 1; j++) {
// console.log(arr, arr[j], arr[j + 1]);
//always compare one to the next one, that is why j+1
if (sortedArray[j] > sortedArray[j + 1]) {
//swap the value
var temp = sortedArray[j];
sortedArray[j] = sortedArray[j + 1];
sortedArray[j + 1] = temp;
//update state
updateDataset(sortedArray.slice())
}
}
}
}
Is there a way to update the state along with how many times the looping occur, so that the app re-render each round of looping
JS only has one event loop and blocking code (like a loop) is going to be The One Thing that JS is doing. Any state changes will be queued until the event loop is free.
What you could do is replace the loop with a recursive function which calls itself with a timer:
const example = (data, countdown) => {
const newData = doStuffWith(data);
if (countdown > 0) {
setTimeout(example, 500, newData, countdown - 1);
}
}
… but be very very careful you don’t end up with two instances of example running overlapped (from separate calls to whatever runs it for the first time).
I have four different button effects where each effect are declared in a variable.
Therefore, I bring all of these four variables and place them within an array called arr in which is used in the clickByItself() function using Math.floor(Math.random()) methods.
Without the for loop, the code clicks by itself randomly in one of the four buttons every time I reload the page.
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
$(arr[random]).click();
}
However, using the for loop I am not being able to make these clicks one-by-one within the maximum of 10 times.
var blueButtonEffect = code effect here;
var redButtonEffect = code effect here;
var greenButtonEffect = code effect here;
var yellowButtonEffect = code effect here;
var arr = [blueButtonEffect, redButtonEffect, greenButtonEffect, yellowButtonEffect];
//will click on buttons randomly
function clickByItself() {
let random = Math.floor(Math.random() * arr.length)
var i;
for (i = 0; i < 10; i++) {
$(arr[random]).click();
setTimeout(clickByItself(), 1000);
}
}
The final output with the current code above is the four buttons being clicked at the same time, not one-by-one.
So, how can I have this function to press a random button by 10 times one-by-one with one second of interval from each click?
To fix your code you need:
A base case for your recursion
Pass a function reference to setTimeout. Currently, you are executing clickByItself and passing its return value (which is undefined) to setTimeout.
Do not use setTimeout in a loop without increasing the time by a factor of i, as the for loop will queue all the function calls at the same time
Alternatively, you can use a "times" argument to avoid looping
You could try something like
function clickByItself(times = 0) {
let random = Math.floor(Math.random() * arr.length)
$(arr[random]).click();
if (++times < 10) {
setTimeout(function(){clickByItself(times);}, 1000);
}
}
An example with console logs
https://jsfiddle.net/pfsrLwh3/
The problem is that the for loop calls the setTimeout 10 times very quickly. If you want to wait until the previous function call finishes prior to calling the next, then you should use recursion or just use a setInterval.
Recursion:
function clickByItself(numIterations) {
let random = Math.floor(Math.random() * arr.length)
let i;
$(arr[random]).click();
if( numIterations < 10 ){
setTimeout(() => {
clickByItself(numIterations += 1)
}, 1000)
}
}
clickByItself(0);
With setInterval
let numIterations = 0;
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
let i;
$(arr[random]).click();
numIterations += 1;
if( numIterations > 10) {
clearInterval(loop)
}
}
let loop = setInterval(test2, 1000);
Are you saying this is working for only 4 times but I think your above code will run in an infinite loop as you are calling clickByItself() again in the for loop.
If you want press a random button by 10 times one-by-one with one second of interval from each click then replace the for loop with
for (i = 0; i < 10; i++) {
setTimeout($(arr[random]).click(), 1000);
}
I'm iterating over an array with a some loop to see if a time is in the past but I also want to return how many times the loop has run, how do I do that?
arr.some(function(a, i){
var date = Date.parse(a.start);
var now = new Date().getTime();
console.log(i);
return now-date < 0;
});
Edit: Solved this by just doing a for loop instead.
You can't return multiple objects in javascript. However, you can return a list of objects.
return [now-date < 0, i]
Count = 0;
arr.some(function(a, i){
Count = +Count + 1;
var date = Date.parse(a.start);
var now = new Date().getTime();
console.log(i);
return now-date < 0;
});
You could be able to make a counter, something like this.. basicly set count to 0 first and everytime the loop runs, count = old count + 1.
I want to cycle through an array and display each element individually, and then remove it. Sort of like this fiddle, but I don't want it to go forever.
I tried using jQuery because I thought it would be easier, but I am clearly missing something. Can anyone help?
Here is my attempt, but it just goes straight to the last element in the array.
var list = [1,2,3,4,5,6];
var length = list.length;
for(i = 0; i < length; i++) {
$('#nums').html(list[i]).delay(750);
}
Oh, and I don't care if it's jQuery or vanilla JavaScript. Either is fine by me.
$(document).ready(function(){
var list = [1,2,3,4,5,6];
var length = list.length;
var i = 0;
var ivl = setInterval( function () {
if (i < length) {
$('#nums').html(list[i]).delay(750);
i++;
}
else {
clearInterval(ivl);
}
}, 750);
});
The (pretty clever) example uses the fact that the modulus operator (%) gives you remainder, which is periodic, so you can use it to cycle through your array by taking the remainder of an infinitely increasing number divided by the length of your list.
If you want it to stop, however, you can just do a check to see if you've finished cycling through the list:
var list = [1, 2, 3, 4, 5, 6];
var length = list.length;
var i = 0;
var finished = false;
function repeat() {
if (!finished) {
document.getElementById('nums').innerHTML = list[i % length];
i++;
if (i === length) {
finished = true;
}
} else {
clearInterval(interval);
}
}
var interval = setInterval(repeat, 750);
<div id="nums"></div>
Late to the party but wouldn't it be better to use setTimeout rather than setInterval just in case the code executed on each iteration takes longer than the interval duration? I mean, I know it's not an issue in this instance but it's just a better/safer way to do this sort of thing.
$(document).ready(function(){
var list = [1,2,3,4,5,6];
var length = list.length;
var i = 0;
(function next(){
if (i < length){
$('#nums').html(list[i++]);
setTimeout(next,750);
}
})();
});
http://jsfiddle.net/zLexhdfp/3/
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);
}