- 실행 컨텍스트 (3) - 콜 스택, 마이크로 태스크2025년 01월 17일
- redpome
- 작성자
- 2025.01.17.:12
자바스크립트에서 실행 컨텍스트는 크게 스택과 큐로 나눌 수 있다. 싱글 스레드인 자바스크립트 엔진 특성상 병렬처리를 하기 위해 비동기적 프로그래밍을 통해 코드를 처리할 수 있다.
const foo = () => { console.log("Execute foo"); return Promise.resolve("foo"); }; async function bar() { console.log("Before await"); const fooValue = await foo(); console.log(fooValue); console.log("After await"); } console.log("First"); bar(); console.log("Second");
위의 코드의 결과를 직관적으로 예측하기 어려운 이유도 이와 같다.
자바스크립트의 런타임 환경은 코드의 실행, 대기열에 존재하는 작업을 처리하는 이벤트 루프에 기반하여 처리한다. 자바스크립트 엔진에서 실행 컨텍스트 스택(콜 스택)이 비었는지 확인하고 비었을 때 태스크 큐에 있는 것을 불러와 콜 스택에 넣는다. 이렇게 되면 싱글 스레드여서 병렬처리가 불가능한 자바스크립트가 비동기 작업을 동시적으로 처리할 수 있다.Run-in-completion은 싱글 스레드인 자바스크립트의 특성처럼 작업 처리에 있어서 비선점인 특징을 가지게한다. 쉽게 말해 다른 작업이 처리되기 전까지는 다른 작업을 처리할 수 없다.
실행 컨텍스트가 쌓이다보면 이전에 처리하던 실행 컨텍스트는 블로킹 된다. 기존에 실행하는 컨텍스트를 멈추고 콜 스택 상위에 존재하는 실행 컨텍스트를 처리한다.
위의 코드를 시간 순서에 따라 처리되고 출력되는 과정은 다음과 같다.
시간에 따른 실행 상태
위의 사진에서 시간 별로 콜스택에 쌓이는 컨텍스트와 비동기 처리되는 경우의 마이크로태스크 큐에 존재하는 작업을 볼 수 있다.
첫 번째 동기 코드인 First가 출력이 되고나서 bar()를 호출하는데 이 때 bar는 async 선언이 되어있지만 일반 함수처럼 실행 컨텍스트에 올라가고 Before await를 출력하게된다.
이후 await선언이 된 변수 fooValue에 의해 foo()함수가 호출되고 foo()의 내부의 실행이 발생한다. 이 시점에서 Execute foo가 출력된다. 이후 console.log(fooValue)는 프로미스를 반환하며, 프로미스는 마이크로태스크 큐에 올려지게 되고 await 선언이 된 코드에 의해 bar() 컨텍스트의 실행은 동작을 멈춘다.
이후 전역에서 Second가 출력된다. 이 시점에서 모든 콜 스택에 실행 컨텍스트가 없어진다, 따라서 마이크로태스크 큐의 작업들이 처리가 되며 프로미스의 처리 결과인 foo가 출력된다. 마지막으로는 await에 의해 실행이 중지되었던 컨텍스트가 실행되고 After await의 출력을 마지막으로 코드 실행이 끝난다.
참고로 마이크로태스크 큐와 매크로 태스크 큐가 존재하는데 이 둘의 차이는 다음과 같다.
- 매크로 태스크 - 비동기 API인 콜백이 추가, setTimeout, setInterval
- 마이크로 태스크 - 프로미스 핸들러, async 함수, process.nextTick(), queueMicrotask()
마이크로 태스크가 매크로 태스크보다 우선 순위를 가진다.
다른 예제도 함께 살펴보자.
function foo() { try { setTimeout(() => { throw new Error("Catch me"); }, 0); } catch (e) { console.log("catch"); } } foo();
이 코드는 스크립트 실행이 중단된다. foo()함수가 호출되고 새로운 실행 컨텍스트로서 콜 스택에 추가된다. 이후 try문을 거쳐 setTimeout에 의해 콜백 함수는 매크로 태스크 큐에 등록되고, 현재 실행 중인 콜 스택의 코드가 끝난 후에야 이벤트 루프에 의해 처리된다.
이렇게 등록되고 난 뒤 foo함수는 종료된다. 콜 스택이 비워졌으므로 매크로 태스크 큐의 작업을 적재하여 실행한다.
throw new Error("Catch me")가 발생한다. 그러나 이 에러는 try...catch 블록이 있는 컨텍스트가 아닌 다른 실행 컨텍스트에서 발생했으므로 try...catch 처리가 불가능하다. 처리되지 않은 에러는 전역 컨텍스트로 전파되며 에러를 잡을 수 없어 스크립트가 중단된다.다음은 다른 예제이다.
async function bar() { try { await baz(); } catch (e) { console.log("catch"); } } async function baz() { console.log("hi"); throw new Error("Catch me"); } bar();
bar()가 실행 컨텍스트로서 콜 스택에 적재된다. 이후 bar 함수는 비동기 함수로서 선언된 것을 알 수 있으며 try문을 실행한다.
await 선언된 baz()가 호출되고 baz에서 hi를 출력한다. Error인 Catch me는 프로미스로서 reject 상태가 된다.
reject 상태인 프로미스이므로 try...catch 블록으로 가고 catch 블록에 의해 catch가 출력된다.'내일배움캠프' 카테고리의 다른 글
포켓몬 도감 만들기 -(1) (0) 2025.02.05 TIL - 트러블 슈팅 (0) 2025.01.23 TIL - Scope, this 바인딩, 클래스 (2) 2025.01.15 개인과제 - 영화 정보 사이트 (0) 2025.01.14 TIL - 프로미스와 메서드 (1) (1) 2025.01.10 다음글이전글이전 글이 없습니다.댓글