[JS] setTimeout 의 콜백 함수 실행 시점

ziwon.k·2021년 10월 18일
0

[JS] 자바스크립트

목록 보기
6/9
post-thumbnail

최근 페어프로그래밍을 하며 setTimeout, setInterval 함수를 여러 번 사용하였는데, 새롭게 알고 된 내용을 정리해보고자 한다.
아직 비동기에 대해 학습하지 않은 상태이기도하고 timer 관련 함수들도 처음으로 사용하다보니 제대로 이해하지 못한 개념이 있었다.
바로 setTime의 호출 시점이다.
다음 문장은 맞는 문장일까?🤔

setTimeout(print, 3000);
print 함수는 3초 후 호출 된다. (?)

정확히 말하자면 print 함수의 호출 시점이 정확히 3초 후라는 것을 보장할 수 없기 때문에 위 문장은 틀렸다.


1. 이벤트 기반 프로그래밍

먼저 자바스크립트의 이벤트에 대해 간략히 알아보자.
사용자가 클릭하는 마우스, 입력하는 키보드 등의 행위는 모두 이벤트라고 할 수 있다. 우리는 이러한 이벤트에 대응하여 각 이벤트에 맞는 적절한 행동을 취해주어야 한다. 그러나 우리는 사용자가 언제 이벤트를 발생시킬지 알 수 없는데, 다행히 브라우저는 이를 감지할 수 있다. 때문에 우리는 브라우저에게 특정 이벤트 발생시 처리할 내용을 알려주고 이벤트 처리를 위임한다.

정리하자면 이벤트 기반의 프로그래밍에서는 어떤 이벤트가 발생할 때, 실행할 작업을 미리 등록해두는데 작업 내용이 담긴 함수를 '이벤트 핸들러'라고 하며, 브라우저에게 이벤트 처리를 위임하는 것을 '이벤트 핸들러 등록' 이라고 한다.


2. 이벤트 발생 순서

그렇다면 여러개의 이벤트가 한번에 동시 다발적으로 발생한다면 어떤 순서대로 실행될까? 이벤트 처리 순서를 살펴보기 위해 필요한 개념들을 정리해보자.

2.1 콜스택(Call Stack)

자바스크립트 코드 실행 중, 함수가 호출될 경우 해당 함수는 실행 순서대로 콜 스택으에 쌓이게(push)된다.
이후 함수의 실행이 완료되면 스택에서 제거(pop)된다.

2.2 태스크 큐(Task Queue)

태스크 큐에는 이벤트 핸들러, setTimeout 등의 함수들이 Call Stack으로 이동되기 전 보관되는 곳이다.

2.3 이벤트 루프(Event Loop)

실행할 함수(콜백)들을 관리한다. 콜스택에 실행 중인 함수가 있는지 확인하고, CallStack이 비었다면 태스크 큐에 대기중인 함수들을 CallStack으로 이동시킨다.

const print = () => console.log('Javascript');

console.log('Hello');
setTimeout(print, 3000);
console.log('World!');

그렇다면 위 코드는 어떤 순서로 실행될까?
Hello Wrold! Javascript 의 순으로 실행될 것이다.

setTimeout(callback, delay) 함수는 delay 시간 후 callback 함수를 실행하는 메서드로 알고 있다. 그렇다면 setTimeout의 print 함수는 언제 CallStack에 들어가게 될까? delay 시간으로 전달한 정확히 3초 후 CallStack에 들어가 실행될까?

위 코드에서 setTimeout의 동작 과정을 그림으로 그려보았다.

  1. setTimeout이 CallStack에 쌓인다.
  2. setTimeout이 실행되고 콜백함수인 print 함수를 브라우저(WebAPI)에 전달한다.
  3. timeout이 발생하면, 즉 delay 시간이 지나면 전달받은 콜백함수를 task queue에 전달한다.
  4. CallStack이 비면 task queue의 콜백함수를 CallStack으로 옮긴다.
  5. CallStack의 콜백 함수를 실행한다.

우리는 setTimeout을 이용해 브라우저에게 3초 이후에 실행할 콜백함수를 전달한 다. 브라우저는 카운터 API를 이용해 delay 시간을 카운트하고 우리가 전달한 콜백함수를 실행하기 위해 task queue에 전달한다.
하지만 이때 task queue에서 바로 call stack으로 옮겨질 수 없다.
call stack이 비어있어야만 옮길 수 있다.
그렇다면 어떻게 call stack이 비었는지 알 수 있을까? 이벤트 루프를 통해 알 수 있다.
이벤트 루프는 무한루프를 돌며 call stack이 비었는지 감시하는 역할을 한다.
이벤트 루프를 통해 call stack이 빈 상태인 것을 확인한 후에야 실행하고자 하는 콜백함수가 task queue에서 call stack으로 옮겨질 수 있다.

call stack이 '비면' 콜백함수를 call stack으로 옮길 수 있다는 것은, 우리가 전달한 3초 후에 만약 call stack이 비어있지 않다면, 우리가 전달한 콜백 함수는 3초 뒤에 바로 실행되지 못하고 task queue에 대기해야 한다는 의미다.
따라서 글의 도입에서 말했던 '3초 후에 실행된다' 는 표현은 틀린 표현이다.
'3초 이후의 시점에 실행된다' 라고 표현하는 것이 좀 더 정확할 것이다.

즉, delay 시간으로 설정된 타이머가 만료된 이후 바로 콜백 함수가 실행되는 것을 보장할 수 없고, delay 시간은 테스크 큐에 콜백 함수를 넣는 타이밍을 늦추는 역할이라고 생각해야 한다.
때문에 무조건 delay 타임 이후에 콜백 함수가 실행되기를 기대하여 setTimeout을 사용한다면 자신의 예상과 다른 결과가 발생할 수 있으니 주의해야 한다.


3. 결론

setTimeout은 timeout 발생 시 task queue에 들어가고, 이벤트는 event가 발생한 시점에 task queue에 들어간다. 이후 call stack이 비어있는지 확인될 때까지 대기하였다가 call stack으로 옮겨진다.
이러한 과정이 발생하는 원인은 바로 싱글 스레드인 javascript에서 발생할 수 있는 blocking 문제를 방지하고 병렬 처리를 위함이라고 한다.
아직 비동기에 대해 학습하지 않아 발생 원인을 100% 이해한 것은 아니지만, 미리 공부하며 이벤트 발생 순서와 시점에 대해 이해한 내용이 도움이 될 수 있을 것 같다.

profile
Frontend-Devloper

0개의 댓글