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);
}
Related
Im trying create some type of number generator on webpage. I want to show like five numbers before the generated number show. For better imagine, you can look to google generator. When you click generate, it shows like 3-4 numbers before generated number. I use setInterval or setTimeout but i dont know how it works. My js code:
var button = document.querySelector("button");
button.addEventListener("click",function() {
for (var i = 0; i < 8; i++) {
setInterval(textC,5);
}
});
function textC(){
number.textContent = Math.floor(Math.random() * 1000) + 1;
}
Thanks for every help!
The issue with setInterval() is that it will continue forever unless cleared, causing you to keep generating random numbers. Instead you can use setTimeout(), but set the timeout to change based on the value of i in the for loop. That way, each interval will occur 50 m/s after the other.
See example below:
const button = document.querySelector("button");
const number = document.querySelector("#number");
button.addEventListener("click", function() {
for (let i = 0; i < 5; i++) {
setTimeout(textC, 50 * i);
}
});
function textC() {
number.textContent = Math.floor(Math.random() * 1000) + 1;
}
<p id="number"></p>
<button>Generate</button>
Don't use a loop (why not?). Just nest setTimeout and call it until a predefined threshold is reached. It gives you maximum control.
var button = document.querySelector("button");
var number = document.querySelector("#number");
const nRuns = 12;
const timeout = 100;
let iterator = 0;
button.addEventListener( "click", textC);
function textC(){
number.textContent = `${Math.floor(Math.random() * 1000) + 1}\n`;
iterator += 1;
if (iterator < nRuns) {
setTimeout(textC, timeout)
} else{
iterator = 0;
// you control the loop, so it's time for some extra text after it
number.textContent += ` ... and that concludes this series of random numbers`;
}
}
<p id="number"></p>
<button>Generate</button>
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 want to create a 'random selector' behaviour where the function iterates through an array for a period of time (example: 3 seconds, 5 seconds) while displaying all of array elements fast throughout the iteration until the iteration ends. Just imagine seeing all the elements displayed in a label one after another until it finally stops at an element.
My code so far:
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function () {
place_label.innerText = selected_place;
}, 400);
}
}
This runs through the iteration and displays an element when the loop is done but it doesn't show each iteration's element. How can I amend this?
EDIT
Even if there's 3 elements, the animations must re-iterate through the array until the duration is completed
Your for finishes iterating before the setTimeout runs, then the function passed to setTimeout runs 100 times using the last value of selected_place.
More about this behavior in setTimeout in for-loop does not print consecutive values
Another problem that I noticed is that your setTimeout will trigger after 400ms, since the for loop will finish in about 1 ms, the function passed to setTimeout will trigger 100 times one after another with no delay in between, for this, I replaced the delay argument of setTimeout from 400 to 400 * i.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 100; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
(function(selected_place){
setTimeout(function () {
place_label.innerText = selected_place;
}, 400 * i);
})(selected_place);
}
}
execute_randomizer();
<span id="place_label"></span>
You could use a closure over the value and a different time for each timeout.
var places = ["Curry Leaf", "Subway", "Burger King"];
function execute_randomizer() {
var place_label = document.getElementById("place_label");
for (var i = 0; i < 10; i++) {
var selected_place = places[Math.floor(Math.random() * places.length)];
setTimeout(function (value) {
return function () {
place_label.innerText = value;
};
}(selected_place), i * 100);
}
}
execute_randomizer();
<div id="place_label"></div>
For a first run through, you could show each element and then take a random element at last value.
function execute_randomizer() {
function cb (value) {
return function () {
place_label.innerText = value;
};
}
var place_label = document.getElementById("place_label");
place_label.innerText = '';
for (var i = 0; i < places.length; i++) {
setTimeout(cb(places[i]), 200 + i * 200);
}
setTimeout(cb(places[Math.floor(Math.random() * places.length)]), 200 + places.length * 200);
}
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer();
<div id="place_label"></div>
You should change your loop because right now you go in loop 100 times in maybe one millisecond and order 100 of change text but once again in the same time.
So better is wait for time out 400 ms and then make next iteration.
Please remember time out is async.
I think it might be better if you would put the whole function in a timeout. (I'm not sure and didn't test, but that's what I would try).
What I mean is that you just build a function that creates your randomizer and fills in your field. After that you put a timeout to call the same function again + keep a counter that you pass as parameter.
An example of what I mean below: (didn't test it)
var places = ["Curry Leaf", "Subway", "Burger King"];
execute_randomizer(0); /* Start for the first time */
function execute_randomizer(times) {
if(times == 100)
return; /* stop execution */
var place_label = document.getElementById("place_label");
var selected_place = places[Math.floor(Math.random() * places.length)];
place_label.innerText = selected_place;
setTimeout(function () {
execute_randomizer(times+1)
}, 400);
}
I'm building a simon game. And after each round the player should see the moves he must play in the next round. So i created a function showMoves which flashes the square he has to play. The problem is that the function is not showing anything. Can anyone tell me what did i miss?
// the effect
function flasher(index) {
$(moves[index]).fadeIn(50).fadeOut(50).fadeIn(50).fadeOut(50).fadeIn(100);
}
var interval2;
// show the moves that supposed to be played
function showMoves() {
for (var i = 0; i < moves; i++) {
if (i === 0) {
interval2 = setTimeout(flasher(i), 1000);
} else {
interval2 = setTimeout(flasher(i), (i+1) * 1000);
}
}
}
setTimeout accepts a function as a first parameter. I assume that by calling flasher you tried to avoid this situation. In you case, this should be done like this:
function showMoves() {
for (var i = 0; i < moves; i++) {
if (i === 0) {
interval2 = setTimeout(function(i) {return function() {flasher(i)}}(i), 1000);
} else {
interval2 = setTimeout(function(i) {return function() {flasher(i)}}(i), (i+1) * 1000);
}
}
}
The setTimeout and setInterval are a little diffrent than we think about them.
They are save some event on specified times that will be fired in its times. Because of this they has a problem with loops:
for(i=0;i<3;i++)
{
setTimeout(function(){alert(i)}, i*1000);
}
after ending the loop the browser has 3 jobs to do:
alert(i) after 1 second
alert(i) after 2 seconds
alert(i) after 3 seconds
But what is the value of 'i'. If you are in c# programming after ending the loop 'i' will be disposed and we have not that.
But javascript does not dispose 'i' and we have it yet. So the browser set the current value for i that is 3. because when 'i' reaches to 3 loop goes end. Therefor Your browser do this:
alert(3) after 1 second
alert(3) after 2 seconds
alert(3) after 3 seconds
That is not what we want. But if change the above code to this:
for(i=0;i<3;i++){
(function (index)
{
setTimeout(function () { alert(index); }, i * 1000);
})(i);
}
We will have:
alert(0) after 1 second
alert(1) after 2 seconds
alert(2) after 3 seconds
So as Maximus said you mast make the browser to get value of i currently in loop. in this way:
setTimeout(function(i) {return function() {flasher(i)}}(i), (i+1) * 1000);
i does not leave out until end of loop and must be get value just now.
What I can derive from your code is that moves is an array, but you're using it as if it's an integer in the for loop. And that's why nothing happens at all.
Replace:
for (var i = 0; i < moves; i++) {
With:
for (var i = 0; i < moves.length; i++) {
And you should see things happening.
But you will notice flasher is called immediately, without timeout. And that's because the result of flasher is set to be called, instead of flasher itself.
Other answers here suggest using an wrapper function, but this requires workarounds to correctly pass the index to the function called by setTimeout.
So assuming that it doesn't have to run in IE8 and below, the following is the most concise solution:
setTimeout(flasher.bind(null, i), (i+1) * 1000)
Full working example:
var moves = [1, 2, 3, 4];
function flasher(index) {
console.log('move', moves[index]);
}
var interval2;
// show the moves that supposed to be played
function showMoves() {
for (var i = 0; i < moves.length; i++) {
interval2 = setTimeout(flasher.bind(null, i), (i+1) * 1000);
}
}
showMoves()
Is it possible to increase the time out limit for JavaScript?
If I have a script that executes for more than 20/30 seconds chrome will pop-up with the unresponsable page dialog.
Making a more efficient script won't help me because the script sometimes need to iterate through a function for a million or billion times
To split the function on steps/chunks and run those inside setInterval(function(){}).
This way page will be responsive, you will be able to notify user about progress of execution and you will get your job done.
UPDATE: Here is simple function that takes
worker function executing each iteration,
chunksz - number of iteration running in single chunk
maxit - total number of iterations.
function task(worker, chunksz, maxit)
{
var idx = 0;
var xint = null;
function exec_chunk()
{
for(var n = 0; n < chunksz; ++n)
{
if(idx >= maxit) { return; }
worker(idx++);
}
setTimeout(exec_chunk,1);
}
exec_chunk();
}
Here is an example : http://jsfiddle.net/Ed9wL/
As you see you get all iterations in order.
UPDATE2:
Say you have a loop:
for(var i=0; i<100000; ++i) { ... do something ... }
then you need to wrap body of the loop into a function and call the task above with it like this:
task(function(i){ ... do something ... },100, 100000);
or like this:
function loopBody(i){ ... do something ... }
task(loopBody,100, 100000);
When you have lots of processing to do client side, you need to split out your work into separate threads. The browser only has a single thread for handling user input (events) and for processing JS. If you're processing too much JS, without yielding, the UI becomes unresponsive and the browser is not happy.
How can you allow your script to yield? The new way is to use web workers http://www.whatwg.org/specs/web-workers/current-work/ . This works by creating a separate thread to run your JS, thread thread does not access to the DOM and can be run concurrently.
However, this newer technology doesn't exist in all browsers. For older browsers, you can split up your work by having the script call itself through timeouts. Whenever a timeout occurs, the script is yielding to the browser to run its events, once the browser is done, your next timeout will be triggered.
Example http://jsfiddle.net/mendesjuan/PucXf/
var list = [];
for (var i = 0; i < 500000; i++) {
list.push(Math.random());
}
function sumOfSquares(list) {
var total = 0;
for (var i = 0; i < list.length; i++) {
total += list[i] * list[i];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Sync temp value = " + total;
document.body.appendChild(node);
}
return total;
}
function sumOfSquaresAsync(arr, callback) {
var chunkSize = 1000; // Do 1000 at a time
var arrLen = arr.length;
var index = 0;
var total = 0;
nextStep();
function nextStep() {
var step = 0;
while (step < chunkSize && index < arrLen) {
total += arr[index] * arr[index];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
index++;
step++;
}
if (index < arrLen) {
setTimeout(nextStep, 10);
} else {
callback(total);
}
}
}
sumOfSquaresAsync(list, function(total) {console.log("Async Result: " + total)});
//console.log("Sync result" + sumOfSquares(list));
The example on jsfiddle has the synchronous call commented out, you can put it back in to see the browser come to a crawl. Notice that the asynchronous call does take a long time to complete, but it doesn't cause the long running script message and it lets you interact with the page while calculating (select text, button hover effects). You can see it printing partial results on the pane to the bottom right.
UPDATE http://jsfiddle.net/mendesjuan/PucXf/8/
Let's try to use c-smile's task function to implement sum of squares. I think he's missing a parameter, a function to call back when the task is finished. Using task allows us to create multiple chunked functions without duplicating the work of calling setTimeout and iteration.
/**
* #param {function} worker. It is passed two values, the current array index,
* and the item at that index
* #param {array} list Items to be traversed
* #param {callback} The function to call when iteration is finished;
* #param {number} maxit The number of iterations of the loop to run
* before yielding, defaults to 1000
*/
function task(worker, list, callback, maxit)
{
maxit = maxit || 1000;
var idx = 0;
exec_chunk();
function exec_chunk()
{
for(var n = 0; n < maxit; ++n)
{
if(idx >= list.length) {
callback();
return;
}
worker(idx, list[idx]);
idx++;
}
setTimeout(exec_chunk,1);
}
}
function sumOfSquaresAsync(list, callback)
{
var total = 0;
// The function that does the adding and squaring
function squareAndAdd(index, item) {
total += item * item;
// DOM manipulation to make it take longer and to see progress
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
}
// Let the caller know what the result is when iteration is finished
function onFinish() {
callback(total);
}
task(squareAndAdd, list, onFinish);
}
var list = [];
for (var i = 0; i < 100000; i++) {
list.push(Math.random());
}
sumOfSquaresAsync(list, function(total) {
console.log("Sum of Squares is " + total);
})
If your goal is to suppress "Kill-Wait" message as quick temporary fix for your slow JavaScript then the solution is to open Tools/Developer Tools in Google Chrome and keep it open and minimized somewhere on your desktop while browsing .