Javascript는 싱글스레드 기반 언어입니다. 따라서 한 순가에 하나의 작업만 처리할 수 있습니다.
아래는 자바스크립트 웹 브라우저가 작동하는 구조 입니다.
자바스크립트를 웹 브라우저에서 작동하기 위해서는 JS 엔진, Web APIs, Callback Queue(Task Queue), Event Loop 영역이 필요합니다.
결론을 간단히 말하자면, JS 엔진에서는 단일 호출 스택(Call Stack)을 이용하여 동기적으로 요청을 처리하고 나머지 영역에서 웹 브라우저 환경 속에서의 자바스크립트가 비동기적으로 처리할 수 있게 지원해주는 역할을 합니다.
즉, 자바스크립트가 비동기적으로 동작하는 동시성은 JS 엔진을 구동하는 환경인 웹 브라우저나 Node.js에서 지원합니다.
1. 자바스크립트 엔진
가장 먼저 자바스크립트 엔진을 보겠습니다.
자바스크립트 엔진은 Memory Heap과 Call Stack으로 구성되어 있습니다.
- Memory Heap
메모리 할당이 일어나는 곳입니다. 프로그램에서 선언한 변수, 함수, 객체 모든 메모리 할당은 여기서 발생합니다.
- Call Stack(호출 스택)
코드가 실행될 때 함수의 호출을 스택 형식으로 저장하는 자료구조 입니다. 호출 스택은 스크립트에서 현재 어떤 함수가 동작하고 있는 지, 그 함수 내에서 어떤 함수가 동작하는 지, 다음에는 어떤 함수가 호출되어야 하는지 제어하는 지 등을 제어하고 기록합니다.
호출 스택이 하나기 때문에 자바스크립트가 단일 쓰레드 기반 언어라고 불리는 이유입니다. 따라서 동기적으로 한 번에 한 작업만 처리할 수 있습니다. 스택 형식으로 쌓이기 때문에 가장 최근에 호출된 작업부터 차례로 실행합니다. 하나의 작업이 끝나면 pop()하고 그 다음 차례의 함수나 코드를 실행합니다. 작업은 차례대로 실행되므로 하나의 작업이 끝날 때까지 또 다른 작업을 실행하지 않습니다.
2. Web APIs
Web APIs부터는 자바스크립트를 비동기적으로 작동시킬 수 있도록 지원하는 역할입니다.
Web APIs는 브라우저에서 자체 제공하는 API로, 비동기 작업 등을 실행할 수 있는 DOM, setTimeout, Promise 등이 있습니다.
Call Stack에서 실행된 비동기 함수는 Web API를 통해 호출하고, Web API는 콜백 함수를 Callback Queue에 push합니다.
비동기적으로 자바스크립트가 수행되는 구조는 아래 그림과 같습니다.
1. 코드가 Call Stack에 쌓인 후, 비동기 함수는 Web API에게 위임합니다.
2. Web API는 비동기 작업을 수행하고, 콜백 함수를 Callback Queue에 push합니다.
3. 이벤트 루프는 Call Stack에 비어있을 때, Callback Queue에 대기하고 있던 콜백 함수를 Call Stack으로 push합니다.
4. Call Stack에 쌓인 콜백 함수가 실행되고, Call Stack에서 pop 됩니다.
3. Callback Queue
비동기적으로 실행된 콜백함수가 보관되는 영역으로, 선입선출(FIFO)로 출력됩니다.
예를 들어, setTimeout에서 타어머가 완료 되고 실행되는 함수(첫번째 인자), addEventListener에서 click 이벤트가 발생했을 때 실행되는 함수(두번째 인자) 등이 저장됩니다.
Callback Queue에는 Task Queue, Microtask Queue, Animation Frames가 있습니다. Web API에서 비동기 작업들이 실행된 후 호출되는 콜백함수들이 기다리는 공간이며, 이벤트 루프가 정해준 순서대로 위치하게 됩니다.
- Task Queue
setTimeout, setInterval, setImmediate, I/O, UI 렌더링 등의 콜백 함수가 저장됩니다.
- MicroTask Queue
ES6에 들어오면서 새로운 컨셉인 MicreTask Queue가 도입되었습니다. Promise, Object.observe 등의 콜백 함수가 저장됩니다.
- Animation Frames
requestAnimationFrame 콜백 함수가 저장됩니다.
* 위에서 Task Queue에 setTimeout이 있고, MicroTask Queue에는 Promise가 대표적으로 있다는 것을 알았습니다. 그렇다면 Event Loop에서 콜백 함수를 꺼내서 처리할 때 어떤 콜백 함수를 더 빨리 처리할까요? 정답은 MicroTask Queue입니다. 아래 코드를 통해 확인할 수 있습니다. 위의 세가지 중 우선 순위가 높은 순으로 나타내면 다음과 같습니다.
4. Event Loop
이벤트 루프는 Call Stack이 다 비워지면 Callback Queue에 존재하는 함수를 Call Stack 에 push 합니다.
이외의 javascript 개념들과 함께 알아보는 동작원리
먼저 아래 개념들을 알고 위의 동작원리에 맞춰서 언제 어느때에 아래 일들이 일어나는지 정리해보겠습니다.
1. 호이스팅
변수 및 함수의 선언이 스코프 내의 최상단으로 끌어올려지는 것 같은 현상을 말하는데, 이는 자바스크립트 엔진이 코드 실행 전에 변수와 함수의 메모리 공간을 미리 할당해주기 때문입니다.
2. this
일반적으로 this는 전역 객체를 가르킵니다. 일반함수의 this 는 전역 객체을 가르킵니다. 또는 함수 호출시에는 앞의 객체를 가리킵니다. 화살표함수의 this 는 어디서 사용되던지 상위 스코프의 this를 가르킵니다.
3. 실행컨텍스트
실행 컨텍스트는 변수 객체, 스코프 체인, this 정보가 담겨있습니다.
자동으로 전역 컨텍스트가 생성된 후 함수 호출시마다 함수 컨텍스트가 생성되고, 컨텍스트 생성이 완료된 후에 함수가 실행됩니다. 함수 실행 중에 사용 되는 변수들을 변수 객체 안에서 값을 찾고 값이 존재하지 않는다면 Lexical 환경의 외부환경레퍼런스로를 통해 Scope 체인을 따라 올라가면서 탐색합니다. 함수 실행이 마무리가 되면 해당 컨텍스트는 사라지고, 페이지가 종료되면 전역 컨텍스트도 사라집니다.
4. 렉시컬 스코프
렉시컬 스코프(Lexical scope)란 함수가 선언되는 위치에 따라서 상위 스코프가 결정되는 개념을 의미하며, 다른 말로 정적 스코프(Static scope)라고도 부릅니다.
5. 렉시컬 환경
렉시컬 환경(Lexical Environment)은 변수 식별자, 해당 변수에 바인딩 된 값, 스코프 체인을 포함하는 자료 구조입니다. 함수를 호출할 때 마다 새로운 렉시컬 환경이 생성되며, 함수의 실행 컨텍스트에 대한 정보를 담고 있습니다. 그리고 함수 실행이 종료되면 해당 렉시컬 환경은 제거됩니다.
6. 스코프 스코프 체인
스코프 : 스코프란 변수(식별자)에 접근할 수 있는 유효한 범위를 뜻합니다.
- 전역 스코프(Global scope) : 코드 어디에서든지 접근 가능
- 함수 스코프(Local scope)
- 함수 내에서만 유효한 범위를 갖게 하는 스코프
- 전역 스코프완 반대되는 개념으로 지역 스코프(Local scope)라고도 불림
- 블록 스코프(Black scope) : 블록단위 { } 내에서만 유효한 범위를 갖게 하는 스코프
스코프 체인 : 현재 스코프에서 식별자를 검색할 때 상위 스코프로 연쇄적으로 찾아나가는 방식을 의미합니다. 변수를 참조할 때 자바스크립트 엔진은 스코프 체인을 통해 해당 변수를 참조하는 코드의 스코프부터 상위 스코프 방향으로 이동하며 선언된 변수를 검색합니다.
7. 이벤트 루프
- 자바스크립트의 이벤트 루프는 단일 스레드에서 실행되는 비동기 작업을 처리하는 메커니즘입니다.
- Micro task queue에는 Promise, async/await 과 같은 작업들이 들어가고, Macro task queue에는 Web API (setInterval, setTimeout)와 같은 작업들이 들어갑니다.
- 이벤트 루프는 Call stack을 확인하고, Call stack이 비어있는 경우 Micro task queue의 작업을 Call stack으로 옮깁니다. 그리고 Micro task queue가 비어있는 경우, Macro task queue의 작업을 Call stack으로 옮기고 처리합니다.
- 자바스크립트의 이벤트 루프는 단일 스레드에서 실행되는 비동기 작업을 처리하는 메커니즘입니다. 이벤트 루프는 Call stack, Micro task queue, Macro task queue로 구성됩니다.
총 정리,,,
1. 코드 실행 시 전역 컨텍스트 생성됩니다. 이때, 호이스팅이 일어나 memory heap 에 변수, 함수, 객체가 할당됩니다. 전역컨텍스트에는 this 정보와 스코프 체인과 변수 객체가 담겨 있습니다. 이 함수는 call Stack 에 push() 됩니다.
2. 그리고 전역 컨텍스트 내부에 함수 호출 시 함수 컨텍스트 또한 생성, call Stack에 push() 됩니다.
3. 위 실행 컨텍스트들은 모두 본인의 렉시컬 환경을 생성합니다.(함수 본인 식별자, 바인딩 된 값, 스코프 체인 이 저장됩니다.)
4. Call Stack 에서 실행된 비동기 함수는 Web API 를 호출하고, Web API 는 이를 Callback Queue에 push() 합니다.
5. 이벤트 루프는 Call Stack 이 비워지면 Callback Queue 의 함수를 Call Stack 에 push() 합니다.
6. Call Stack에 쌓인 함수가 실행되고, Call Stack에서 pop 됩니다.
틀린 부분이 있다면 알려주시면 감사하겠습니다!!! ㅜㅜ
참고 링크
https://bbangson.tistory.com/89
https://frontend-interview-question.vercel.app/
'JavaScript' 카테고리의 다른 글
[Bun] Bun 이란? Bun 설치 및 사용법 (0) | 2024.04.08 |
---|
댓글