DEV Community

Discussion on: What’s your favorite JS interview question?

Collapse
 
ufocoder profile image
Sergey Ivanov

What will output these example? Why?

for (var i=0; i < 10; i++){
    setTimeout(function(){
        console.log(i);
    }, 1000);
}
Enter fullscreen mode Exit fullscreen mode

How to fix it to output numbers from 0 to 9?

Collapse
 
vaishnav_holkar profile image
Vaishnav Holkar • Edited

In for loop replace var with let.As let has block scope .It creates new scope of i for each iteration and each setTimeout callback will have its own i value.
Code -->
Method 1 ->By using let

for (let i=0; i < 10; i++){   
 setTimeout(function(){
        console.log(i);
    }, 1000);
}
Enter fullscreen mode Exit fullscreen mode

Method 2->By using IIFE function

for(var i=0;i<10;i++){
    (function IIFE (j){
    setTimeout(function (){
    console.log(j);
    },j*1000);
    })(i);
}
Enter fullscreen mode Exit fullscreen mode

Method 3 ->

for(var i=0;i<10;i++){
    let j=i;
    setTimeout(function (){
        console.log(j);
    },j*1000);
}

Enter fullscreen mode Exit fullscreen mode
Collapse
 
fervero profile image
Maciej Bójko • Edited

Fun trivia: setTimeout can have more than two arguments. After the callback function, and the duration, you can pass args for the callback, as well. Instead of working around setTimeout, use setTimeout.

for (var i = 0; i < 10; i++) {
   setTimeout(console.log, 100*i, i); 
}

(Although I don't even write the for loop, like, ever. It's all map, filter, reduce etc. So I'd rewrite it into something like this:

new Array(10)
   .fill(0)
   .map((x, i) => i)
   .forEach(x => setTimeout(console.log, 100*x, x))
Collapse
 
raslanove profile image
raslanove

for (var i=0; i<10; i++) {
setTimeout(function() { console.log(i++ - 10); }, 1000);
}

Collapse
 
bharath_bheemireddy profile image
Bharath_Bheemireddy

Using let you will get output numbers from 0 to 9.

Collapse
 
jakebman profile image
jakebman

Feel free to run it and prove me wrong, but I believe that it prints 10 ten times, at 1 second intervals.

It's due to binding on the variable I, not the value of I when you create the lambda.

You need to introduce a local variable in the loop body to fix it.

Thread Thread
 
akashkava profile image
Akash Kava

You are wrong, if you use for(let i instead of for(var i it will print 0 to 9 correctly. jsfiddle.net/uh86qx1v/1/

Thread Thread
 
hiattzhao profile image
Hiatt

Can you explain why that happens? I'm new to ES6 and learned that let has block scope. When used in the for loop, in conjunction with setTimeOut set to 1000, you would think the console.log would run every 1000 ms for each console.log. But this doesn't happen. When I tried it, the sequence of console.logs appear all at once after 1000 ms. Why does that happen? Is it because setTimeOut, when called 10 times, gets pushed to the stack, then after 1000 ms the stack does its thing and all 10 calls are executed simultaneously?

If you take out the setTimeOut and just do console.log within the for (var i loop you get the correct result as well.

Thread Thread
 
jakebman profile image
jakebman

Yup, you're correct. The callbacks are registered very close to each other - they're all queued up to run at basically the same time.

Thread Thread
 
jakebman profile image
jakebman

Akash - this is the local variable I was trying to describe: jsfiddle.net/e7gyc5ts/1/ (I needed an IIFE to solve a problem that is better solved by let)

Thread Thread
 
ufocoder profile image
Sergey Ivanov
Thread Thread
 
bharath_bheemireddy profile image
Bharath_Bheemireddy
Collapse
 
genocideaon profile image
Pranat

It is an excellent question to test how much you understand JS.

Collapse
 
cilice profile image
Alexander Plavinski

Why is this your favourite question?

Collapse
 
ufocoder profile image
Sergey Ivanov

Because answer on this question will discover what answerer know about:

  • Scopes
  • Closures
  • How event loop works in the browser
Collapse
 
sarathsnair profile image
Sarath S Nair • Edited
  • Using let

for(let i=0; i<10; i++) {
setTimeout(()=>console.log(i),i*500);
};

  • Using bind

for(var i=0; i<10;i++) {
setTimeout(console.log.bind(null,i), i*100);
};

  • Or you can use IIFE

for(var i=0; i<10; i++) {
(function(x){
setTimeout(()=> console.log(x), x*100)
})(i);
};

Collapse
 
kenbellows profile image
Ken Bellows

On the bind example, heads up that in some older browsers you need to pass console as the first argument to any console.<method>.bind(). Not that this is a particularly real-world thing to do, but it's bitten me a few times when trying to debug a promise chain in an older browser; I always love to do promise.then(console.log), but in old browsers this breaks until you do promise.then(console.log.bind(console)).

Collapse
 
vsrikanth49 profile image
Srikanth • Edited

for (var i = 0; i < 10; i++) {
setTimeout(
(function (i) {
console.log(i)
})(i),
1000
)
}

Collapse
 
vsrikanth49 profile image
Srikanth

for (var i = 0; i < 10; i++) {
setTimeout(
(function (i) {
console.log(i)
})(i),
1000
)
}