Calling setTimeout function within a loop - javascript

I'm new to javascript and am trying to call a function using setTimeout from within a for loop. The loop executes for each member of a nodeList.
I'm finding that the function I'm calling with setTimeout is only actually executing during the last iteration of the loop. In the example below, I would like to make three separate calls to setTimeout but I'm finding that the first two calls are ignored.
function moveants(e, stepdistance) {
. . . . .
for(var i = 0; i < 3; i++)
{
var nextAnt = antgroup.childNodes[i]
nextAnt.count = 0;
nextAnt.member = i;
setTimeout(function () { takeStep(nextAnt, mouseclickX, mouseclickY, 10) }, 0);
}
}
function takeStep(ant, destX, destY, stepDistance) {
. . . .
. . . .
if( condition )
{
return;
}
else
{
takeStep(ant, destX, destY, stepDistance);
}
}
I have seen other posts that describe making multiple calls to setTimeout. Amazingly (to me), the multiple calls will work if I simply take them out of the for loop like this.
setTimeout(function () { takeStep(antgroup.childNodes[0],
mouseclickX, mouseclickY, 10) }, 10);
setTimeout(function () { takeStep(antgroup.childNodes[1],
mouseclickX, mouseclickY, 10) }, 10);
setTimeout(function () { takeStep(antgroup.childNodes[2],
mouseclickX, mouseclickY, 10) }, 10);
I just can't figure out why there is a difference between calling them from within a for loop and calling them outside of one.
I am getting valid return values from the setInterval call in every case.. it's just that with only the last iteration of the for loop does the function actually execute.
Thanks in advance for any help.

nextAnt will be overwritten on every loop, so takeStep() will be called 3 times, but always with the same arguments.
You may try this instead:
(function(a,b,c){
setTimeout(function(){
takeStep(a,b,c,10)}, 0);
})(nextAnt, mouseclickX, mouseclickY);

If your not setting a delay, then why bother with setTimeout?
In the loop your delay is set to 0, outside the loop you've used 10. Also, outside the loop you've assigned values to count and member, but not outside the loop?
Try this:
function moveants(e, stepdistance)
{
for (var i = 0; i < 3; i++)
{
setTimeout("takeStep(antgroup.childNodes[i], mouseclickX, mouseclickY, 10)", 0);
}
}
or this:
function moveants(e, stepdistance)
{
for (var i = 0; i < 3; i++)
{
takeStep(antgroup.childNodes[i], mouseclickX, mouseclickY, 10);
}
}

Related

Javascript repeat a function x amount of times

I'm trying to develop a function that repeats a function x amount of times, just once, not based on settimerinterval or settimeout or anything based on time. I don't want to use a while/for loop directly, I want to use this repeat function.
I've tried something like this:
function repeat(func, times) {
for (x = 0; x < times; x++) {
eval(func)
}
}
But eval doesn't work on a function.
const func = () => console.log("hi");
const times = 3;
Array.from({length: times}, () => func());
I define a function.
I set the number of times to repeat function.
I make an array the size of times to repeat function.
I run the "defined function" on each element of the array.
Just call func and decrement counter and call the function repeat again.
function repeat(func, times) {
func();
times && --times && repeat(func, times);
}
repeat(function () { document.write('Hi<br>'); }, 5);
If Lodash is an option, then _.times
You can also define a reusable function, utilizing setInterval and clearInterval
function runFunctionXTimes(callback, interval, repeatTimes) {
let repeated = 0;
const intervalTask = setInterval(doTask, interval)
function doTask() {
if ( repeated < repeatTimes ) {
callback()
repeated += 1
} else {
clearInterval(intervalTask)
}
}
}
function sayHi() {
console.log("Hi")
}
The following line will run sayHi 5 times without wasting any time between completion of one and the beginning of another.
runFunctionXTimes(sayHi, 0, 5)
It is also possible to pass function arguments to setInerval, you can read more about it here https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval
Using recursion:
function repeat(fn, times) {
var loop = function (times) {
if (times) {
fn(times);
loop(--times);
}
}
loop(times);
}
repeat(function (times) {
console.log(times);
}, 5);
You may not be able to eval() a function, but you can call it. Here's the fix:
function repeat(func, times) {
for (x = 0; x < times; x++) {
func()
}
}

Add a pause/interval to every iteration in a FOR LOOP

I want to write a for loop which prints number 1 to 10 with intervals after every iteration "like this"
How can I achieve it? I tried sleep() setInterval() setTimeout(") and what not but can't seem to find any working solution. And if possible I would like to do this using pure Javascript only.
function abc(){
for(i=1;i<=10;i++){
document.write(i+"<br>");
sleep(1000);
}
}
To answer the question, to get something like a sleep function you could just write somehting like this as a helper function
function sleep(dur) {
var d = new Date().getTime() + dur;
while(new Date().getTime() <= d ) {
//Do nothing
}
}
console.log(new Date().getTime())
sleep(1000)
console.log(new Date().getTime())
Then you could call the sleep function after every iteration like
function abc(){
for(i=1;i<=10;i++){
document.write(i+"<br>");
sleep(1000);
}
}
But Note that sleep will freeze your browser in this time and
you don't really wan't this kind of behaviour when you just want to periodiccally do sth
window.setInterval would be what you want in such cases
function abcd(i){
document.write(i + "<br>")
}
function repeatedTimeout(func,times,duration) {
var args = Array.prototype.slice.call(arguments).splice(3);
var i = 0;
args.push(i)
var wrap = function () {
if(args[args.length - 1] >= times)
window.clearInterval(wrap)
else {
func.apply(this,args)
args[args.length - 1]++
}
}
window.setInterval(wrap,duration)
}
repeatedTimeout(abcd,10,1000)
Which would call it 10 times every 1000 milliseconds, whithout freezing the Browers
Heres the JSBin
Update
If it really has to be a for loop, you could do something like this,
regardless of the sense it makes to me
for (var i = 0; i <= 10 ; i++) {
window.setTimeout(
(function (i){
return function() {
document.write(i + "<br>")
}
})(i),i * 1000)
}
In this case heres another JSBin
This would call window.setTimeout in a for loop and a multiple of the timeout with i as the timeout,
this would work, but i'd rather suggest using setInterval like you already did in the Fiddle you posted in the comment
Due to the mostly asynchronous (and single threaded) nature of JavaScript in the browser, constructs such as sleep() aren't the way to go.
You can write a generic function using setTimeout() that will do the looping and then pass in the function that should be run at every interval of x milliseconds. At least you'd have a reusable container in which you can run your code.
function loopn(n, fn, delay)
{
if (n > 0) {
fn();
if (n > 1) {
setTimeout(function() {
loopn(n - 1, fn, delay);
}, delay);
}
}
}
loopn(10, function() {
console.log('hello there');
}, 1000);
You could deconstruct the loop into a recursive function and use setTimeout to implement the pause.
var i = 0;
var limit = 10;
function loop(){
console.log(i);
i++;
if(i < limit)
{
setTimeout(loop, 100);
}
}
loop();
​

How to pass variable to anonymous function

I want to pass variable setTimeoutfunction and do something with that. When I alert value of i it shows me numbers that i did not expected. What i m doing wrong? I want log values from 1 till 8.
var end=8;
for (var i = 1; i < end; i ++) {
setTimeout(function (i) {
console.log(i);
}, 800);
}
The standard way to solve this is to use a factory function:
var end=8;
for (var i = 1; i < end; i ++) {
setTimeout(makeResponder(i), 800);
}
function makeResponder(index) {
return function () {
console.log(index);
};
}
Live example | source
There, we call makeResponder in the loop, and it returns a function that closes over the argument passed into it (index) rather than the i variable. (Which is important. If you just removed the i argument from your anonymous function, your code would partially work, but all of the functions would see the value of i as of when they ran, not when they were initially scheduled; in your example, they'd all see 8.)
Update From your comments below:
...will it be correct if i call it in that way setTimeout(makeResponder(i),i*800);?
Yes, if your goal is to have each call occur roughly 800ms later than the last one, that will work:
Live example | source
I tried setTimeout(makeResponder(i),setInterval(i));function setInterval(index) { console.log(index*800); return index*800; } but it's not work properly
You don't use setInterval that way, and probably don't want to use it for this at all.
Further update: You've said below:
I need first iteration print 8 delay 8 sec, second iteration print 7 delay 7 sec ........print 2 delay 2 sec ...print 0 delay 0 sec.
You just apply the principles above again, using a second timeout:
var end=8;
for (var i = 1; i < end; i ++) {
setTimeout(makeResponder(i), i * 800);
}
function makeResponder(index) {
return function () {
var thisStart = new Date();
console.log("index = " + index + ", first function triggered");
setTimeout(function() {
console.log("index = " +
index +
", second function triggered after a further " +
(new Date() - thisStart) +
"ms delay");
}, index * 1000);
};
}
Live example | source
I think you now have all the tools you need to take this forward.
Your problem is that you are referring to the variable i some time later when your setTimeout() function fires and by then, the value of i has changed (it's gone to the end of the for loop. To keep each setTimeout with it's appropriate value of i, you have to capture that value i separately for each setTimeout() callback.
The previous answer using a factory function does that just fine, but I find self executing functions a little easier than factory functions to type and follow, but both can work because both capture the variables you want in a closure so you can reference their static value in the setTimeout callback.
Here's how a self executing function would work to solve this problem:
var end=8;
for (var i = 1; i < end; i ++) {
(function (index) {
setTimeout(function() {
console.log(index);
}, 800);
})(i);
}
To set the timeout delay in proportion to the value of i, you would do this:
var end=8;
for (var i = 1; i < end; i ++) {
(function (index) {
setTimeout(function() {
console.log(index);
}, index * 800);
})(i);
}
The self executing function is passed the value of i and the argument inside that function that contains that value is named index so you can refer to index to use the appropriate value.
Using let in ES6
With the ES6 of Javascript (released in 2015), you can use let in your for loop and it will create a new, separate variable for each iteration of the for loop. This is a more "modern" way to solve a problem like this:
const end = 8;
for (let i = 1; i < end; i++) { // use "let" in this line
setTimeout(function() {
console.log(i);
}, 800);
}
The main reason for this to not to work, is because, of the setTimeout which is set to run after 800 and the scope of i.
By the time it executes which the value of i will already have changed. Thus no definitive result could be received. Just like TJ said, the way to work this around is through a handler function.
function handler( var1) {
return function() {
console.log(var1);
}
}
var end = 8;
for (var i = 1; i < end; i++) {
setTimeout(handler(i), 800);
}
Demo
setTimeout accepts variables as additional arguments:
setTimeout(function(a, b, c) {
console.log(a, b, c);
}, 1000, 'a', 'b', 'c');
Source.
EDIT: In your example, the effective value of i will likely be 8, since the function is merely to be called after the loop has finished. You need to pass the current value of i for each call:
var end=8;
for (var i = 1; i < end; i ++) {
setTimeout(function (i) {
console.log(i);
}, 800, i);
}

Greasemonkey delay... setTimeout doesnt work ok

I've been playing around with a site, in which I want to continue clicking a button for i amount of times every interval seconds.
My code is:
clickbidBtn1 = function() {
var bidBtn=document.getElementById("BidButton");
var interval = 15000;
for (var i=3; i>=0; i--){
setTimeout(bidBtn.click(1);,i*interval);
};
I've found out that GM executes all i amount of clicks at the same time, not with the intended delay. is there a way to delay the time of click? Say i wanted the function to click the button every 15 second for i amount of times.
I was thinking of giving it some more variables, and adding one variable in the settimeout code part, which only executes # the click, then comparing increased variables with current ones before going to the next settimeout... but haven't thought it through yet... it seems to be a complicated process for a simple process... :( i wll play around with it a bit
Use setInterval() for this.
One way:
var bidClickTimer = 0;
var numBidClicks = 0;
function clickbidBtn1 ()
{
var interval = 15000;
bidClickTimer = setInterval (function() {BidClick (); }, interval);
}
function BidClick ()
{
numBidClicks++;
if (numBidClicks > 3)
{
clearInterval (bidClickTimer);
bidClickTimer = "";
}
else
{
bidBtn.click (1);
}
}
clickbidBtn1 ();
Alternatively, without using global vars:
function clickbidBtn1 ()
{
var interval = 15000;
this.numBidClicks = 0;
this.bidClickTimer = 0;
this.BidClick = function () {
numBidClicks++;
if (numBidClicks > 3)
{
clearInterval (bidClickTimer);
bidClickTimer = "";
}
else
{
bidBtn.click (1);
}
};
this.bidClickTimer = setInterval (function(thisScope) {thisScope.BidClick (); }, interval, this);
}
clickbidBtn1 ();
Just to explain why your code does not work: You are calling the .click method immediately (putting () after a function name calls the function) and actually passing the return value of that function to setTimeout. The for loop is so fast that everything seem to happen at the same time.
You have to pass a function reference to setTimeout, e.g. an anonymous function:
setTimeout(function() {
bidBtn.click(1);
}, i*interval);

Create a pause inside a while loop in Javascript

I would like to create a pause inside a while loop so that I can create n animations that each appear 3 seconds after the other.
I've tried the following, but it doesn't work. Would love to have someone show me what I'm doing wrong.
i=0;
while (i < n) {
someanimation();
setTimeout(function(){
i++;
}, 3000);
};
setTimeout does not pause; it asks Javascript to run some other code later.
Googling for "setTimeout loop" tells you exactly what you need to know. If you look around a little bit, it even mentions setInterval. The difference: using setTimeout to loop will wait 3 seconds in between loops, whereas setInterval will make it take 3 seconds total for the loop (including however much time the animation takes, as long as it's less than 3 seconds :) ). Also, setInterval constructs an infinite loop that you'll have to break out of after the desired number of times; setTimeout requires you to construct the loop yourself.
i = 0;
// setTimeout approach
function animation_loop() {
someAnimation();
setTimeout(function() {
i++;
if (i < n) {
animation_loop();
}
}, 3000);
};
animation_loop();
// setInterval approach
i = 0;
someAnimation();
iid = setInterval(function() {
i++;
if (i < n) {
someAnimation();
} else {
clearInterval(iid);
}
}, 3000);
setTimeout is a little trickier than that because it doesn't block (i.e. it doesn't finish waiting on the timeout before continuing with the program).
What you want is closer to this:
var i = 0;
function nextFrame() {
if(i < n) {
someanimation();
i++;
// Continue the loop in 3s
setTimeout(nextFrame, 3000);
}
}
// Start the loop
setTimeout(nextFrame, 0);
It may also be worth your while to read up on setInterval as a possible alternative.
Well, thanks to ES6-7 with Promises we can now make a pause and make it look nice at the same time!
var id = 0;
async function do() {
while(true) {
await pause(id);
//will happen only after pause is done
id++;
}
}
function pause(id) {
return new Promise(resolve => setTimeout(() => {
console.log(`pause ${id} is over`);
resolve();
}, 1500));
}
do();
One of the way of doing it is to use RxJS. Please take a look at working example here
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
create a function like:
function sleep_until (seconds) {
var max_sec = new Date().getTime();
while (new Date() < max_sec + seconds * 1000) {}
return true;
}
and then change your code to
i=0;
while (i < n) {
someanimation();
sleep_until(3);
do_someotheranimation();
};
You are not very specific about what you want to do, but I'd say the main problem is that you call someanimation() without a delay. So maybe this solves it for you:
for (var i = 0; i < n; i++) {
setTimeout(someanimation, 3000 * i);
};
Note the missing () after someanimation as it is the callback for setTimeout().
function myFunction() {
var x;
for(var i=0;i<10;i++){
if (confirm("Press a button!") == true) {
x = "You pressed OK!";
} else {
x = "You pressed Cancel!";
}
document.getElementById("demo").innerHTML = x;
}
}``

Categories

Resources