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).
Related
I am attempting to write a dealer function for a card game, I want the function to keep its i value each time its called, so that that the card pile( player1Deck and player2Deck) gets smaller and smaller till empty. currently the i value looks like it will reset each time the function is called how can I stop this.
I am very new to all of this.
function dealOut(){
for (let i = 0; i < player1DeckHandcells.length; i++) {
const cell = player1DeckHandcells[i];
cell.appendChild(player1Deck.cards[i].getHTML)
}
for (let i = 0; i < player2DeckHandcells.length; i++) {
const cell = player2DeckHandcells[i];
cell.appendChild(player2Deck.cards[i].getHTML)
}
}
More information
Each dealout call should make i increase by 7. this means we are on the 8th "card"(player1Deck.cards[i].getHTML and player2Deck.cards[i].getHTML) in the "deck"( player1Deck and player2Deck)and there should be a total of 28 cards with 21 left to go. I want the i value to count through these "cards", so it doesn't just repeat the first 7 every time dealout is called.
You can use a closure (a function that keeps a copy of the outer variables). We can use that function for dealOut. Note: arrays are zero-indexed so i starts at 0 not 1.
function loop() {
// Maintain a count
let count = 0;
// Return a function that we actually use
// when we call dealOut. This carries a copy
// of count with it when it's returned that we
// can update
return function () {
const arr = [];
// Set i to count, and increase until
// i ia count + 7
for (let i = count; i < count + 7; i++) {
arr.push(i);
}
console.log(arr.join(' '));
// Finally increase count by 7
count += 7;
}
}
const dealOut = loop();
dealOut();
dealOut();
dealOut();
dealOut();
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);
}
So I'm trying to make this coin flip but it keeps flipping forever... when I want it to stop after 10 times. I also need a counter variable that tells me how many times it is flipped.
var coin = randomNumber (0,1);
write (coin);
while (coin < 10) {
coin = randomNumber (0,1);
write (coin);
}
The easiest way is to just use a for loop.
for (var i = 0; i < 10; i++) {
var coin = randomNumber (0, 1);
write (coin);
}
See this for more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration
If you want to stick to the while loop:
var timesFlipped = 0;
while (timesFlipped < 10) {
var coin = randomNumber (0, 1);
write (coin);
timesFlipped = timesFlipped + 1; // alternatively: timesFlipped++;
}
You haven't shown us your randomNumber function, but it's likely that it only produces numbers that are less than 10. Since your while loop says to keep going as long as coin is less than 10, the loop goes forever.
while loops are notorious for resulting in infinite loops. I personally never use them. Since you know how many times you need to loop, a counting loop is the correct choice.
Here's what you need:
// Write the function that gets random number and report the results a certain number of times:
function coinToss(numTimes) {
// Instead of a while loop, use a counting loop that will
// have a definite end point
for(var i = 0; i < numTimes; i++){
// Get a random number from 0 to 1
var coin = Math.floor(Math.random() * 10);
// Test to see if it is even or odd by checking to see if
// it is divisible by 2 with no remainder.
var even = (coin % 2 === 0);
// Report the results
console.log("The coin was " + (even ? "heads " : " tails"));
}
}
// Now, call the function and tell it how many times to loop
coinToss(10);
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.
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.