본문 바로가기
카테고리 없음

자바스크립트 작동원리: stack, queue (feat. single thread, event loop, memory heap, 비동기/동시성

by 찬찬2 2022. 10. 5.

[1] stack
stack의 사전적 의미는 "쌓는다"이다. 그렇다면 무엇을 쌓는가? 바로 함수이다. stack의 맨 위에 데이터를 추가하는 것을 "push"라고 하고 반대로 맨 위에 데이터를 하나씩 제거하는 것을 "pop"이라고 한다. 하나의 탑에 블록을 계속 쌓아가는 형태이고, 가장 마지막에 올린 데이터가 가장 먼저 나오는 형태라고 해서 "Last-In-First-Out(LIFO)" 형태를 같고 있다.


[2] single thread
thread는 하나의 stack을 가지고 있고 stack은 선형구조이기 때문에 중간에 다른 작업이 끼어들 수가 없다. 선형구조란 데이터를 입력/출력할 때 정해진 순서에 의해 순차적으로 작업되는 구조를 말한다.

[3] 자바스크립트의 동시성(비동기 처리)
JS의 특징들 중 비동기, 동시성 등 상반되는 개념들이 등장한다. "하나에 하나만 처리할 수 있다며? 어떻게 동시성을 가질 수 있는거지?" 이는 자바스크립트의 동작원리의 핵심인 Web API, Event Loop와 Queue에 있다.

웹 API는 자바스크립트가 아니다. 브라우저 소속(Event Loop, Queue)이다.
✔ 웹 API란 개발자가 브라우저 상에 쉽게 개발할 수 있도록 도와주는
객체모음들입니다.
웹 API를 이용해서 돔요소를 조작하거나 일부 영역을 업데이트 시키거나 비디오나 오디오나 그래픽 요소를 사용할 수 있습니다.
웹 API는 객체 기반으로 동작하고 엔트리 포인트가 존재합니다

[4] Event Loop 와 Queue(microtask, task)
Event Loop에서 Loop의 사전적인 의미는 "반복, 순환"이다. 무엇을 반복하고 순환할까? 바로 stack과 queue를 반복적으로 그리고 순환하면서 코드를 실행하는 것을 말한다고 볼 수 있다.
call stack이 비워있는 경우 queue에서 작업을 꺼내어 call stack에 넣는다. 자바스크립트는 이 Event Loop와 queue들을 이용하여 비동기 작업을 수행한다. 직접적인 작업은 Web API에서 처리되고, 그 작업들이 완료되면 요청시 등록했던 callback 함수가 queue(microtask queue 또는 task queue)에 등록된다.
Event Loop는 queue에 쌓인 callback 함수들을 순서대로 앞에서 부터 꺼내어 처리한다. Event Loop는 stack에서 더이상 처리할 작업이 없을 경우 우선적으로 ①microtask queue를 확인한다. microtask queue에 작업이 있다면 microtask에 있는 작업을 꺼내서 call stack에 넣는다. 만약 microtask의 queue가 비어서 더 이상 처리할 작업이 없으면 이때 ②task queue를 확인한다. task queue에 있는 콜백함수도 꺼내서 call stack에 넣는다. 이렇게 Event Loop와 queue는 비동기 코드들을 처리하는데, 이 러한 과정이 자바스크립트 엔진으로 하여금 동시성을 가지는 것 처럼 보이게 한다.

[5] Microtast Queue
대상: Promise, async/await, process.nextTick, Object.observe, MutationObserver과 같은 비동기 호출을 넘겨받는다.

[6] Task Queue
대상: setTimeout(), setInterval(), setImmediate()와 같은 task를 넘겨받는다.

Event Loop가 비동기 작업을 처리하는 우선순위는 Microtask Queue → Animation Frames Task Queue 순이다
- 참고링크


[7] async / await의 조금 다른 실행순서
keyword: suspend(의미: 연기하다, 유예하다, 미루다)

 

await에 의해 실행순서가 조금 달라진 것을 볼 수 있다.

 

그림1


실행순서1) console.log("before function")이 stack에서 쌓인 후 바로 pop되어 실행된 후 stack에서 삭제되었다. (비동기와 관련없는 함수이기 때문에)

 

그림2


실행순서2) myFunc함수가 호출되어 stack에 쌓인다.
실행순서3) myFunc함수 안에 있는 console.log("In function!") 함수가 stack에 쌓이고 바로 pop되어 실행된 뒤 stack에서 삭제되었다.

 

그림3


실행순서4) 아직 myFunc함수 안에 남아 있는 코드들을 실행해야 한다. one함수가 stack에 쌓이고 Promise.resolve 함수가 호출되어 stack에 쌓인다.
실행순서5) stack에 마지막에 쌓인 것 순으로, Promise.resolve함수가 실행되면서 stack에서 삭제되었고 one함수가 순차적으로 stack에서 삭제된다.
실행순서6) 여기서 중요하다. one함수가 실행된 후 one의 프로미스 결과값을 res 변수에 할당하려고 할때 자바스크립트 엔진은 "await" 키워드를 만나 async 함수 myFunc 나머지 코드를 모두 "microtask queue"에 보내버린다.
바로 이부분에서 자바스크립트 엔진은 myFunc함수를 suspend(연기하다, 미루다) 한 것이다. 만약 await 키워드가 없었다면 myFunc함수 안의 코드들은 순차적으로 실행되어 stack에 쌓인 후 순차적으로 실행되었을 것이다.

 

그림4


실행순서7) 글로벌에 있는 나머지 코드인 console.log("After function!")이 stack에 쌓이고 바로 실행되어 stack에서 삭제된다.

 

그림5


실행순서8) 마지막으로 stack이 모두 비웠을때 Event loop는 microtask queue를 우선적으로 바라보고 담겨져 있던 나머지 muFunc함수 안의 코드들을 실행하고 stack에세 삭제한다.

만약 HTTP 요청의 작업을 동기로 수행했다면 해당 함수가 stack에 쌓인채로 머물것이고, 자바스크립트 엔진은 해당 작업이 끝날때까지 어떠한 작업도 수행할 수 없습니다. 즉, 동기 작업이 다른 코드들을 블로킹한 것입니다. 그러나 자바스크립트는 비동기 작업들을 Web API에게 넘겨줌으로써, 해당 작업이 완료될때까지 다른 코드들을 실행할 수 있습니다. 이것이 바로 논블로킹입니다.



참고링크:
https://sculove.github.io/post/javascriptflow/

https://velog.io/@titu/JavaScript-Task-Queue%EB%A7%90%EA%B3%A0-%EB%8B%A4%EB%A5%B8-%ED%81%90%EA%B0%80-%EB%8D%94-%EC%9E%88%EB%8B%A4%EA%B3%A0-MicroTask-Queue-Animation-Frames-Render-Queue

https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke#syntax

댓글