티스토리 뷰

Web

웹에서 '싱글 스레드(Single-thread)' 환경에서 비동기 호출이 가능한 이유가 무엇일까?

바로 '비동기 프로그래밍 모델'과 '이벤트 루프(Event-loop)' 개념 덕북에 가능한 것이다. 이 두 가지 개념을 통해 싱글 스레드 환경에서도 동시에 여러 작업을 수행할 수 있게 된다.

 

싱글 스레드

'싱글 스레드' 환경에서 하나의 스레드만을 사용하여 모든 작업을 처리한다. 즉, 한 번에 하나의 작업만 수행할 수 있다. 브라우저의 자바스크립트 엔진은 기본적으로 싱글 스레드로 동작한다. 이는 동시에 여러 작업을 수행하지 않는다는 것이다.

 

비동기 호출

'비동기 호출'은 어떤 작업이 시작된 후, 그 작업이 완료되기까지 기다리지 않고 다른 작업을 계속 수행하는 방식을 말한다. 비동기 호출의 예로는 서버에 데이터를 요청하고, 응답이 오기를 기다리는 동안 다른 작업을 계속 수행하는 것이 있다.

 

비동기 프로그래밍 모델

자바스크립트가 싱글 스레드 환경에서도 동시에 여러 작업을 처리할 수 있도록 도와준다. 이 모델은 '콜백 함수', 'Promise', 'async / await' 등을 사용해 비동기 작업을 처리한다.

 

이벤트 루프(Event-Loop)

'이벤트 루프'는 자바스크립트의 비동기 작업을 관리하는 핵심 요소이다. 이벤트 루프는 '콜 스택(Call Stack)'과 '태스트 큐(Task-Queue)'를 사용해 비동기 작업을 처리한다.

 

콜 스택(Call Stack)

실행할 함수의 호출 정보를 저장하는 스택이다. 싱글 스레드에서 실행할 작업이 하나씩 쌓이고, 현재 실행 중인 작업이 끝나면 다음 작업이 실행된다.

 

태스트 큐(Task-Queue)

비동기 작업의 콜백 함수들이 대기하는 큐이다. 비동기 작업이 완료되면 해당 콜백 함수가 태스크 큐에 추가된다.

 

이벤트 루프 동작

이벤트 루프는 콜 스택이 비어 있는 상태를 감지하고, 태스크 큐에 대기 중인 작업이 있는 경우 이를 콜 스택으로 이동시켜 실행한다. 이렇게 하여 비동기 작업이 완료될 때까지 기다리지 않고, 다른 작업을 계속 수행할 수 있게 된다.

 

비동기 호출과 이벤트 루프 동작 예제

console.log("Start");

setTimeout(() => {
	console.log("Inside setTimeout");
}, 1000);

console.log("End");

 

  1. `console.log("Start")`: 콜 스택에 들어가서 실행
  2. `setTimeout`: `setTimeout` 함수는 비동기 함수로, 콜백 함수는 태스크 큐에 추가
  3. `console.log("End")`: 콜 스택에 들어가서 실행

 

1초 후, `setTimeout` 콜백 함수가 태스크 큐에 추가된다. 이벤트 루프는 콜 스택이 비어 있음을 감지하고, 태스크 큐의 콜백을 콜 스택으로 이동시켜 실행한다.

 

// 출력 결과
Start
End
Inside setTimeout

 

 

한 번 더 이해하기

[콜 스택]
| Start      | <- 실행
| End        | <- 실행
|            | <- 비어 있음

[태스크 큐]
| setTimeout | <- 대기 중

[1초 후]
[콜 스택]
| setTimeout | <- 실행

[태스크 큐]
|            | <- 비어 있음

 

이벤트 루프가 콜 스택이 비어 있는 것을 감지하면, 태스크 큐에서 대기중인 `setTimeout` 콜백을 콜 스택으로 이동시켜 실행한다.

 

이벤트 루프(Event-Loop)

자바스크립트의 비동기 작업을 처리하는 핵심 메커니즘이다. 자바스크립트는 싱글 스레드 환경에서 동작하기 때문에, 여러 비동기 작업을 동시에 처리할 수 있는 방식이 필요하다. 이를 '이벤트 루프'가 가능하게 해준다. 이 과정을 이해하기 위해 이벤트 루프, 태스크 큐(Event Queue), 콜 스택의 동작 방식을 알아야 한다.

 

콜 스택(Call Stack)

현재 실행중인 함수 호출 정보를 저장하는 스택이다. 함수 호출이 발생하면 호출된 함수가 콜 스택에 쌓이고, 함수 실행이 완료가 되면 콜 스택에서 제거된다.

 

동작 예시

  1. `console.log('A')` 실행. 함수는 콜 스택에 쌓임
  2. `setTimeout`과 같은 비동기 함수 호출. 함수의 콜백은 태스크 큐에 추가
  3. `console.log('B')` 실행. 함수가 콜 스택에 쌓임
  4. `console.log('B')` 함수가 실행된 후 콜 스택에서 제거
  5. 콜 스택이 비어있을 때, 이벤트 루프가 태스크 큐를 확인하고, 대기중인 콜백 함수를 콜 스택으로 이동시켜 실행

 

태스크 큐(Task Queue)

비동기 작업의 콜백 함수들이 대기하는 큐이다. 비동기 작업이 완료되면, 해당 작업의 콜백 함수가 태스크 큐에 추가된다.

 

// 예제 코드
console.log('Start');

setTimeout(() => {
	console.log('Inside setTimeout');
}, 1000);

console.log('End');

 

동작 과정

  1. `console.log('Start')` 실행 뒤 콜 스택에서 제거
  2. `setTimeout` 함수 호출 뒤 함수의 콜백은 태스크 큐에 추가
  3. `console.log('End')` 실행 뒤 콜 스택에서 제거
  4. 1초 후 `setTimeout`의 콜백이 태스크 큐에 추가
  5. 콜 스택이 비어있을 때, 이벤트 루프가 태스크 큐를 확인하고, 콜백 함수를 콜 스택으로 이동시킨 뒤 실행
// 출력 결과
Start
End
Inside setTimeout

 

이벤트 루프(Event Loop)

콜 스택과 태스크 큐를 관리하며, 콜 스택이 비어 있는 상태를 감지하고, 태스크 큐의 대기 중인 작업을 콜 스택으로 이동시켜 실행한다.

 

동작 과정

  1. 콜 스택이 비어 있을 때 → 이벤트 루프가 태스크 큐를 확인
  2. 태스크 큐에서 → 대기 중인 콜백 함수를 콜 스택으로 이동시켜 실행
  3. 콜 스택이 다기 비어 있을 때 → 과정 반복

 

다시 한 번 이해해보자.

[ 콜 스택 ]                 [ 태스크 큐 ]
-------------------           --------------------
| Function A    |           | setTimeout        |
| Function B    |           | Callback Function |
-------------------           --------------------

1. 함수 A와 B가 콜 스택에서 실행됨.
2. setTimeout 콜백이 태스크 큐에 추가됨.
3. 콜 스택이 비어 있을 때 이벤트 루프가 태스크 큐를 확인하고 콜백을 콜 스택으로 이동시킴.

[ 콜 스택 ]                 [ 태스크 큐 ]
-------------------           --------------------
| setTimeout      |           |                  |
| Callback Function|           |                  |
-------------------           --------------------

 

이러한 구조로 자바스크립트는 싱글 스레드 환경에서도 비동기 작업을 효율적으로 처리할 수 있다. 이벤트 루프는 비동기 작업이 완료되면 적시에 콜백을 실행하여 비동기 처리의 일관성을 보장하게 만든다.