Passing parameters to the function in setTimeout - javascript

I want to call a function in a cycle with setTimeout(), and also pass the counter as a parameter. Here's what I tried:
<!DOCTYPE html>
<html><body>
<button onclick="timed1()">Display alerts</button>
<script>
function timed1() {
for (i=1; i<4; i++){
setTimeout(
(function(i){ alert(i) })(i), 1000*i
);
}
}
</script>
</body></html>
Function calls are done with correct values, but immediately instead of the intervals I wanted. I have read this and this SO questions but unfortunately don't see yet what should I change in my case.

You can use let here if you want to bind different values of i to the setTimeout function
function timed1() {
for (let i=1; i<4; i++){
setTimeout(function() {
alert(i)
}, 1000*i)
}
}
timed1()

You're swimming on IIFE (Immediately Invoked Function Expression).
You're calling your function immediately.
setTimeout(
(function(i){ alert(i) })(i), 1000*i
); ^
The scope of your variable i is open, you need to use let keyword.
for (i = 1; i < 4; i++) {
^
Look at this code snippet
function timed1() {
for (let i = 1; i < 4; i++) {
setTimeout(
function() {
alert(i)
}, 1000 * i);
}
}
<button onclick="timed1()">Display alerts</button>

The setTimeout function allows you to pass arguments to the callback function, like so:
function timed1() {
for (i=1; i<4; i++){
setTimeout(alert, 1000 * i, i);
}
}
timed1()
Any arguments after the time are passed along to the callback.

The expression (function(i){ alert(i) })(i) does not return a function for setTimeout to call. If you're going the IIFE way, make sure to return a new function from it:
setTimeout((function(i) {
return function() {
alert(i);
};
})(i), 1000 * i);
With ES2015+ a far better readable equivalent can be realized:
for (let i = 1; i < 4; i++) {
setTimeout(() => alert(i), 1000 * i);
}

Related

function setTimeout in loop

If I use setTimeout in a loop so only after the loop ends it executes all the operation in the same time.
I tried to put setTimeout in a separate function, as suggested by many articles, but it does not help.
The code is very simple:
function foo(i)
{
setTimeout( function() {console.log(i);}, 2000);
}
for (let i=0; i<5; i++)
{
foo(i);
}
It prints
0
1
2
3
4
in one shot
It's because the proccess registry the 5 setTimeout at almost the same time.
Try with the different timeout time like this:
function foo(i)
{
setTimeout( function() {console.log(i);}, 1000 * i);
}
for (let i=0; i<5; i++)
{
foo(i);
}
you could also do it with a generator. a generator can pause midway through a function
https://www.javascripttutorial.net/es6/javascript-generators/
function* generator() {
let index = 1;
while (index <= 5) {
yield index++;
}
}
let f = generator();
var interval = window.setInterval(function(){
const obj = f.next();
if (obj.done){
clearInterval(interval);
} else {
console.log(obj.value)
}
}, 1000);

why is IIFE needed to create a new scope?

From You Don't Know JS:
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
gives
6
6
6
6
6
but using an IIFE like so
for (var i=1; i<=5; i++) {
(function(){
var j = i;
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})();
}
gives
1
2
3
4
5
My question: why doesn't
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
var j = i;
console.log( j );
}, i*1000 );
}
or
for (var i=1; i<=5; i++) {
function timer() {
var j = i;
console.log(j);
}
setTimeout(timer, i*1000 );
}
work like the IIFE example? It seems to me they both have a function declaration with a new variable j, wouldn't that create a new lexical scope with a specific setting for i?
The important part of the IIFE is that it runs right away; before i changes, it reads its value and puts it in a new variable. The function reading i in your other examples – function timer() – does not run right away, and the value it puts in its new variable is the value of i after it’s already changed.
Also, in ES6, you can just let i = … instead of var i = … and it’ll work fine without the IIFE or j:
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
because let has block scope instead of function scope and variables declared in the initialization part of for loops count as being half-inside the for’s block.
i, being declared with var, is hoisted. Variables don't automatically get their scopes bound to an inner function; unless the inner function explicitly has var i or a paramter of i (thus defining a new i bound to the scope of the inner function), i will continue to refer to the hoisted i in the outer scope.
For example, you could do what you were thinking of like this, if you wanted:
for (var i=1; i<=5; i++) {
setTimeout( function timer(i){
console.log( i );
}, i*1000, i );
}
(The third argument to setTimeout is what the function, the second argument, will be called with)
This means that timer will be called with i as it is during iteration, and the function will use a new i, bound to the scope of the function, initialized via the parameter.
It's a pretty bad idea, though - better to use const and let, which have block scope rather than function scope, and better not to shadow outer variables.
This sort of IIFE
for (var i=1; i<=5; i++) {
(function(){
var j = i;
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})();
}
is often written like
for (var i=1; i<=5; i++) {
(function(j){
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})(i);
}
so, you can see that "captured" value is i in this case
You can do the same without IIFE
for (var i=1; i<=5; i++) {
function timer(j) {
setTimeout(function() {
console.log(j);
}, j * 1000 );
}
timer(i);
}
of course, this is equivalent of
function timer(j) {
setTimeout(function() {
console.log(j);
}, j * 1000 );
}
for (var i=1; i<=5; i++) {
timer(i);
}
if using ES2015+, you can use let
for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
Now, if you use a transpiler because you need to support ES5 (or whatever internet exploder supports) you'll see that the transpiled version is
var _loop = function _loop(i) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
};
for (var i = 1; i <= 5; i++) {
_loop(i);
}
Which looks incredibly like the previous version of the code

Delay in for loop

I´m having trouble to delay a function in a for-loop.
So here is my test:
<script type="text/javascript">
var j = 0;
for(var i = 0; i <= 100; i++){
setTimeout(function(){
console.log(i);
},j*2);
j++;
}
</script>
I want that every count will appear step by step with a delay in the console. But currently only 101 appears 101 times. Why and what is a better solution?
Cheers
JavaScript doesn't have block level scoping of variables, so the value of i in any deferred functions will be the last value of i from the loop (in your case, 101). You can either use a named function or an immediately invoked function expression to create a closure that gives the value the correct scope.
<script type="text/javascript">
var j = 0;
for(var i = 0; i <= 100; i++){
(function(i) {
setTimeout(function(){
console.log(i);
},j*2);})(i);
j++;
}
</script>
Also bear in mind that the second argument passed to setTimeout is the delay in milliseconds, so that's a very short delay (a tiny fraction of a second) between each call.
function SetTimeoutLoop(i) {
setTimeout(function() { console.log(i); }, i*2);
}
for(var i = 0; i <= 100; i++){
SetTimeoutLoop(i)
}
JSFIDDLE
u may try this:
<script type="text/javascript">
var j = 0;
for(var i = 0; i <= 100; i++){
(function(i, j){
setTimeout(function(){
console.log(i);
},j*2);
})(i, j);
j++;
}
</script>
It happens as once your callback starts to fire your loop would be finished and the value will be max.
you can do like
(function me(i,j) {
if(i <= 10) {
setTimeout(function(){
alert(i++);
me(i,j);
},j++*2);
}
})(0,0);
Here is a sample fiddle.
You can't do that in for loop, instead you should use a recursive function like this:
here's a fiddle
var j = 0;
var i=0;
setTimeout(function(){
log_i(i);
},j*2);
function log_i(i){
console.log(i);
i++;
if(i<=100){
setTimeout(function(){ log_i(i); },j*2);
}
j++;
}

JavaScript async callback and scope

Consider the following example:
var cb = function (t) {
console.log('callback -->' + t);
};
for(var i = 0; i<3; i++) {
console.log(i);
setTimeout(function(){
cb(i);
},1000);
}
Working example at jsfiddle
The output of this code snippet is:
0
1
2
callback ---> 3
callback ---> 3
callback ---> 3
Everything works as expected, for loop puts 3 callback calls into the event loop. By the end of the for loop i == 3 and when the callbacks get executed all of them print 3 because they contain the link to the i which is 3.
How could this snippet be improved so when the callback gets executed it uses the actual value which was passed to it.
The output should be:
callback ---> 1
callback ---> 2
callback ---> 3
Thanks in advance.
Create a closure so that the setTimeout handler will refer to the closure's local variable (which in this case, we also named i), not the i from the loop:
for (var i = 0; i < 3; i++) {
(function (i) {
console.log(i);
setTimeout(function () {
cb(i);
}, 1000);
}(i));
}
You could try .bind:
for(var i = 0; i<3; i++) {
console.log(i);
setTimeout(cb.bind(null, i),1000);
}
The demo.
The traditional way to handle this is to create a closure:
for(var i = 0; i<3; i++) {
console.log(i);
setTimeout((function(i){return function(){cb(i)}}(i)),1000);
}
A frequent question.
Let's try to use some features of future of JS. I mean let.
It creates local scope variable and you don't need to use a closure or another trick.
But now it works only in FF (i'm using 20.0.1)
for(var i = 0; i<3; i++) {
console.log(i);
let a = i;
setTimeout(function(){
cb(a);
},1000);
}
I will just use setTimeout() in your cb's functoin
var cb = function (t) {
setTimeout(function(){
console.log('callback -->' + t);
},1000);
};
for(var i = 0; i<3; i++) {
console.log(i);
cb(i);
}

Scope issues with for loop

It isn't really a problem for me. I just want to know how I can do it correctly, and not with a workaround. Well, if we use for() and some delayed events, only the last value is considered.
Test: http://jsfiddle.net/39dQV/
// Using only i (does not work)
for(var i=0; i<10; i++) {
setTimeout(function() {
test1.textContent = i;
}, i * 1000);
}
// Private scope to i (does not work either)
for(var i=0; i<10; i++) {
var x = i;
setTimeout(function() {
test2.textContent = x;
}, i * 1000);
}
// Callback scope (workaround)
function set_textContent(i) {
setTimeout(function() {
test3.textContent = i;
}, i * 1000);
};
for(var i=0; i<10; i++) {
set_textContent(i);
}​
What do I need do that to so it works correctly, ie: consider the current value of i, instead of the last value changed by time?
Your solution isn't really a "workaround", it's nearby the best way:
for(var i=0; i<10; i++) {
setTimeout(function() {
test1.textContent = i;
}, i * 1000);
}
Can't work! i is in local scope an not longer defined when the function is executed.
for(var i=0; i<10; i++) {
var x = i;
setTimeout(function() {
test2.textContent = x;
}, i * 1000);
}
Same situation here, x is local in the loop.
You need a own scope for your variable. The only way to define such a scope, is to encapsulate the definition of your timeout function inside a closure or function, like you did in your third way.
I'd write it so:
for(var i=0; i<10; i++) {
( function( i ) {
setTimeout(function() {
test1.textContent = i;
}, i * 1000);
} ( i ) };
}
the closure defines its own scope, so the value of i is stored.
For more deeper information see: http://www.javascriptenlightenment.com/ (I love it)

Categories