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

다음 코드의 결과는??

1
2
3
4
5
6
// 정답
5;
5;
5;
5;
5;

이유

var 키워드로 선언한 변수는 함수 레벨 스코프를 갖는다. for문 코드 블록에서는 전역 변수로 선언되었기 때문에, 변수 i 값이 갱신된다.

setTimeout 함수는 비동기 처리 방식으로 실행된다.

setTimeout 함수는 Web API로 이동하여 타이머가 만료되면 Task Queue로 이동한다.

Task Queue에서 Call Stack이 비워질 때 까지 대기한다.

대기하는 동안, 다음 for 문이 돌고 있으므로, setTimeout 함수가 Task Queue에서 실행 컨텍스트가 비워질 때 까지 계속 대기한다.

이러한 이유로 i가 5가 될 때, Call Stack이 비워지므로 그때서야 이벤트 루프에 의해서 console 창에 출력되는 i 값이 5이므로 5가 5번 찍히게 된다.

해결방법

1. 블록 레벨 스코프

1
2
3
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}

2. 즉시실행함수

1
2
3
4
5
for (var i = 0; i < 5; i++) {
(function (param) {
setTimeout(() => console.log(param), 1000);
})(i);
}

setTimeout 함수를 즉시실행함수로 감싸서 함수 레벨 스코프를 갖도록 해준 뒤 즉시실행함수의 인수로 i를 전달해주고 즉시실행함수 내부의 함수에 파라미터로 해당 인수를 어디서 참조할지 설정해주면 된다. 위 예제에서는 콘솔 로그의 인수로 파라미터를 전달해줘야 할 것이다.