자바스크립트 는 단일스레드 이다.
단일 스레드 (single thread)란 한번에 한가지 일밖에 처리할수없다는것을 의미한다. (다른 말로는 콜스택이 하나라고도한다)
자바스크립트 엔진
JS 엔진은 메모리 힙 (Memory Heap) 과 콜 스택(Call Stack) 으로 구성되어 있다.
메모리 힙(Memory Heap)
객체,변수,함수 등 할당된 메모리,데이터 등이 저장되는곳,
메모리 힙은 런타임 중 생성된 객체와 데이터를 저장하고 관리한다. 힙은 동적으로 크기가 조정되며 객체가 생성되거나 더 이상 참조되지 않는경우 가비지 컬렉션에 의해 해제된다.
가비지 컬렉션이란 더 이상 사용되지 않는 객체들을 자동으로 탐지하여 메모리를 해제하는 과정이다.이는 개발자가 명시적으로 메모리 관리에 신경쓰지 않아도 되는 장점을 제공합니다.
콜 스택 (Call Stack)
자바스크립트 엔진이 함수 호출을 추적하는데 사용되는 데이터 구조를 말한다.
콜 스택은 후입선출(LIFO Last-In-First-Out) 원칙에 따라 동작한다.
함수가 호출되면 해당 함수의 실행 컨텍스트가 콜 스택에 추가된다. 새로운 함수 호출이 발생하면 해당 함수의 실행 컨텍스트가 콜 스택의 맨 위에 추가되고,현재 실행중인 함수는 일시 중단된다. 즉 첫번째 함수 실행 도중 두번째 함수가 호출되면 첫번째 함수는 일시 중단되고 두번째 함수가 실행된다. 두번째 함수가 반환(종료)됐다면 이전에 실행중이던 첫번째 함수가 다시 실행된다.
콜 스택은 자바스크립트의 동기적인 특성을 가지고 있으며, 함수 호출과 실행 순서를 관리하는 아주 중요한 역할을 한다.
콜 스택이 너무 깊게 쌓이거나 지나치게 호출이 많아지면 스택 오버플로우(Stack Overflow) 오류가 발생한다.
콜스택이 한개가 아니라 만약 여러개라면 어떻게될까? 그중 하나의 함수의 처리시간이 다른함수에 비해 오래걸린다면 어떻게될까?
그 특정 함수로 인해 다른 코드를 실행하지 못하게되어 특정함수가 반환될때까지 유저는 어떠한 동작도 할수 없게 된다.
이때 효과적으로 event 들을 관리하기위해 필요한것에 Web API,Callback Queue,event loop 이다.
Web API
Web API 란 웹 브라우저에서 제공하는 자바스크립트 기반 API의 집합입니다. 주로 DOM조작,네트워크요청,타이머 설정 등 다양한 작업을 수행할 수 있습니다.
// DOM을 통해 요소에 접근하여 내용 변경
const element = document.getElementById('myElement');
element.textContent = 'Hello, World!';
// 이벤트 리스너 등록
element.addEventListener('click', () => {
console.log('Element clicked!');
});
// XMLHttpRequest를 사용한 GET 요청
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data');
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
const responseData = JSON.parse(xhr.responseText);
console.log(responseData);
} else {
console.error('Request failed');
}
}
};
xhr.send();
// Fetch API를 사용한 GET 요청
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error));
// 일정 시간 후에 함수 실행 (setTimeout)
setTimeout(() => {
console.log('Delayed function executed');
}, 2000);
// 주기적으로 함수 실행 (setInterval)
const intervalId = setInterval(() => {
console.log('Repeated function executed');
}, 1000);
// 타이머 취소 (clearTimeout 또는 clearInterval)
clearTimeout(timeoutId);
clearInterval(intervalId);
이 외에도 웹 스토리지API,애니메이션API,캔버스API 등 다양한 API가 존재한다.
Evenvt Loop
이벤트 루프는 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 그리고 콜백 큐에 대기중인 함수(콜백함수,이벤트핸들러 등)가 있는지 반복해서 확인한다. 만약 콜 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적(FIFO,First In First Out)으로 콜백 큐에 대기 중인 함수를 콜 스택으로 이동시킨다.
Callback Queue
setTimeout 이나 setInterval 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역.
function foo () {
console.log("foo")
};
function bar () {
console.log("bar")
};
setTimeout(foo,0);
bar();
위와 같은 코드들이 있다고할때 어느 함수가 먼저 실행이 될까?
위 코드들의 실행순서를 나열해보겠다.
- 전역 코드가 실행되기 시작하여 setTimeout 함수가 호출된다. setTimeout 함수의 함수 실행 컨텍스트가 생성되고 콜 스택에 푸시되어 현지 실행 중인 컨텍스트가 된다. Web API 인 타이머 함수도 함수이므로 실행 컨텍스트를 생성한다.
- setTimeout 함수가 실행되면 콜백 함수를 호출 스케줄링 하고 종료되며 콜 스택에서 pop 된다. 이때 호출 스케줄링 이란 타이머 설정과 타이머가 만료되면 콜백함수를 콜백 큐에 푸시 하는것으로 브라우저의 역할이다.
3-1. 브라우저는 타이머를 설정하고 타이머의 만료를 기다린다.타이머가 만료되면 콜백 함수 foo가 콜백 큐에 푸시된다. 위 예제의 경우 타이머가 0 으로 설정되어있어 바로 콜백함수를 호출할것 같지만 지연시간이 4ms 이하인경우 최소지연 시간4ms가 지정된다. 즉, 4ms 후에 콜백 함수 foo가 콜백 큐에 푸시되어 대기하게 된다.
3-2. bar 함수가 호출되어 bar 함수의 함수 실행 컨텍스트가 생성되고 콜 스택에 푸시되어 현재 실행 중인 실행 컨텍스트가 된다.
이후 bar 함수가 종료되어 콜 스택에서 pop된다. 이때 브라우저가 타이머를 설정한 후 4ms가 경과했다면 foo 함수는 아직 큐에서 대기중이다.- 전역 코드 실행이 종료되고 전역 실행 컨텍스트가 콜 스택에서 pop 된다. 이로써 콜 스택에는 아무런 실행 컨텍스트도 존재하지 않게된다.
- 이벤트 루프에 의해 콜 스택이 비어 있음이 감지되고 콜백 큐에서 대기 중인 콜백 함수 foo가 이벤트 루프에 의해 콜 스택에 푸시된다, 다시말해 콜백 함수 foo의 함수 실행 컨텍스트가 생성되고 콜 스택이 푸시되어 현재 실행 중인 실행 컨텍스트가 된다. 이후 foo 함수가 종료되어 콜 스택에서 pop된다.
이처럼 비동기 함수인 setTimeout의 콜백 함수는 태스크 큐에 푸시되어 대기하다가 전역 코드 및 명시적으로 호출된 함수가 모두 종료되면 비로소 콜 스택에 푸시되어 실행된다.
마이크로태스크 큐(microtask queue or job queue)
setTimeout(()=>console.log(1),0);
Promise.resolve()
.then(()=>console.log(2))
.then(()=>console.log(3))
위 코드의 출력 순서는 어떻게 될까? 1 2 3 ? 2 3 1 ?
정답은 2 3 1 순서로 출력된다.
프로미스의 후속 처리 메서드의 콜백 함수는 콜백 큐가 아니라 마이크로태스 큐에 저장된다. 마이크로태스크 큐와 콜백 큐는 별도의 큐다.
프로미스 후속 처리 메서드는 마이크로태스크 큐에 저장되고 그 외의 비동기 함수의 콜백 함수나 이벤트 핸들러는 콜백 큐에 저장된다.
마이크로태스크 큐는 콜백 큐 보다 우선순위가 높다. 즉, 이벤트 루프는 콜 스택이 비면 먼저 마이크로태스크 큐에서 대기하고 있는 함수를 실행한다. 이후 마이크로태스크 큐가 비면 콜백 큐에서 대기하고 있는 함수를 실행한다.
'Javascript' 카테고리의 다른 글
JavaScript - 함수 호출 분석 해보기 (0) | 2023.07.07 |
---|---|
Javascript - 널 병합 연산자 "??" (0) | 2023.06.26 |
JavaScript - Scope (0) | 2023.06.04 |
JavaScript - 호이스팅 (0) | 2023.06.04 |
JavaScript - 클래스(class) 에 대해서 (0) | 2023.05.28 |