본문 바로가기
Web development/Node.js & Typescript

[Javascript] for, setTimeout 퀴즈

by 자몬다 2020. 3. 31.

아래와 같은 코드의 출력이 어떻게 될까?

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

   난 보고.. 1초 있다가 1, 또 1초 있다가 2, 또 1초있다가 3 찍힐거라고 생각했다.

정답은 1초 후 거의 동시에 3이 3번 찍힌다.

 

정답을 보고서, 아.. for문이 너무 빨리 돌아서 그렇구나? 1초안에 3번 도는게 끝나서 3이 세번 찍히는구나 했다.

그리고 await가 걸린것도 아니니 당연히 각 setTimeout은 거의 동시에 실행되니까,

그래서 거의 동시에 찍히는거구나 했다. (이건 당연한건데 왜 생각을 못했을까?)

 

그러면 for문 반복횟수를 늘리고, setTimeout 시간을 줄이면 다른 숫자도 찍히겠구나?

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

// 10000이 만 번 찍힘

아니었다. 

음.. 스코프 때문인가? var를 let으로 바꾸면 1, 2, 3, ... 이렇게 찍히기 때문이다.

 

결론은 시간이나 횟수와는 상관이 없다. 백만번이건 천만번이건..

for (var i = 0 ; i < 10000 ; i++) {
  console.log('a', i)
  setTimeout(() => {
  	console.log('b', i)
  }, 10)
}

이렇게 찍어보면 a 1 a 2 a 3 a 4.... 다 찍히고 나서 b 10000 만번 찍힌다.

이벤트루프에서 setTimeout은 console 찍고나서 실행할 여유가 생기기 때문에 다음 틱에 실행된다.

그래서 어떻게 하건 for문이 다 돈 뒤에 찍히는 것이다.

 

let은 순서대로 찍히는 이유는, 스코프가 블록에 있기 때문이다.

for문 내부에 변수가 있는것이나 다름이 없기 때문에.. 블록 스코프의 i를 참조하게 되어서 그렇다.

for (var i = 0 ; i < 10000 ; i++) {
  const j = i;
  setTimeout(() => {  
    console.log('b', j)  
  }, 10) 
}

위 코드도 let을 쓴것과 같은 결과가 나온다.

아니면 내부에 클로저가 있는거랑도 결과는 같다.

 

반대로 var는 아래와 결과가 같다. 블록 밖의 변수를 사용했기 때문!

let i = 0;
for (i ; i < 10000 ; i++) {
    setTimeout(() => {
        console.log('a', i)
    }, 10)
}

 

댓글