본문 바로가기
V8

await

by adawn 2025. 10. 21.

V8에서 await는 비동기 처리(async/await) 문법을 실행하기 위한 런타임 레벨의 Promise 처리 메커니즘.

 

1. await의 JS 의미 

자바스크립트 언어 레벨에서 await의 경우 다음과 같이 동작함 : 

 

async function foo() {
  console.log(1);
  await new Promise(resolve => setTimeout(resolve, 1000));
  console.log(2);
}

foo();
console.log(3);

 

await는 Promise가 resolve될 때까지 함수 실행을 일시 중단시킴.

await는 async 함수 안에서만 사용 가능함.

awiat expr는 내부적으로 Promise.resolve(expr)를 호출하고 그 결과가 완료될 때 현재 실행 컨텍스트를 종료하고 event loop에 제어를 넘김.

 

실행 흐름

 

1
3
(1초 뒤)
2

 

단계별 호출 과정

 

1. foo() 호출

 

foo는 async 함수이기 때문에 항상 Promise를 반환함.

(함수에 async를 붙이게 되면 함수의 리턴 값이 무엇이든 간에 promise 객체로 감싸져서 반환되는 걸 이야기함.

foo()의 실제 반환값은 Promise이고, 그 안에 123이 resolve된 값으로 들어가 있음.)

즉 foo()를 호출하면 즉시 Promise 객체가 반환되지만 그 내부의 비동기 처리는 나중에 진행됨.

 

 

그래서 값을 꺼내려면 

 

foo().then(value => console.log(value)); // 123

 

이런 식으로 promise에서 값을 꺼내줘야 함. 

 

2. console.log(1)

 

함수가 시작되면 첫 줄이 즉시 실행되서 1을 출력함. 

 

3. await new Promise(resolve => setTimeout(resolve, 1000))

 

이 안에서 new promise(...)은 즉시 실행이 됨. 

여기서 setTimeout(resolve , 1000)이 예약됨.

즉 1초 후 resolve()가 호출될 예정. 

 

await은 이 Promise가 resolve될 때까지 현재 foo 함수의 실행을 일시 중단시킴.

 

하지만 이건 스레드를 멈추는게 아닌 foo의 나머지 부분 (console.log(2))를 나중에 실행해줘라고 마이크로태스크 큐에 등록함.

 

4.foo()는 일단 종료됨.

await에서 멈췄기 때문에 foo()는 아직 완전히 끝나지 않았음. 

하지만 JS 엔진 입장에선 지금 당장 실행할 코드는 없으므로 

foo()는 Promise를 반환하고, 그 Promise는 pending 상태임.

 

5.console.log(3)

 

foo()가 멈춘 동안 , 메인 스레드는 계속 다음 줄로 가서 console.log(3)을 출력함.

 

6.1초 후 setTimeout -> resolve()

 

1초가 지나면 setTimeout의 콜백이 실행되어 resolve가 호출됨.

이떄 v8은 foo가 기다리던 promise가 완료된 걸 감지하고 await 이후의 코드(console.log(2))를 마이크로태스크 큐에 등록함.

 

7.마이크로 태스크 큐 실행. 

현재 실행중인 코드가 모두 끝나면 (event loop의 다음 tick)

마이크로태스트 큐에 있는 foo의 남은 부분이 실행됨. 

console.log(2)가 출력되고 이제 foo()가 반환한 Promise는 fulfilled 상태가 됨.

 

2.Promise 기본 개념

Promise는 나중에 완료될 수도 있는 작업을 표현하는 객체임. 

 

지금은 결과를 모르지만 언젠가 성공(resolve)하거나 실패(reject)할 수도 있다 라는 약속을 이야기함.

 

예를 들어 

 

const p = new Promise((resolve, reject) => {
  setTimeout(() => resolve('완료!'), 1000);
});

 

이 promise는 1초 후에 완료로 성공(resolve)됨.

결과를 받으려면 .then()을 사용함. :

 

p.then(result => console.log(result)); // 1초 후 "완료!" 출력

 

3.promise 체인 

여러 개의 비동기 작업을 순서대로 실행하고 싶을 때, then()을 연속적으로 연결하는 걸 promise 체인이라고 부름.

 

예를 들어,

로그인 -> 데이터 불러오기 -> 결과 출력 과정을 순차 실행한다고 해보면 

 

promise 체인 예시 

 

login()
  .then(user => getUserData(user))
  .then(data => processData(data))
  .then(result => console.log(result))
  .catch(err => console.error(err));

 

여기서 중요한 점 : 

 

then()은 항상 새로운 Promise를 반환함.

따라서 .then()을 계속 이어서 쓸 수 있음. -> 이게 체인임.

각 .then() 블록은 이전 작업의 결과값을 다음 단계로 전달함.

 

그래서 코드가 아래처럼 동작함.

 

login()  →  getUserData()  →  processData()  →  console.log()

 

4.Promise 체인 내부 동작(V8)

V8에선 이 체인을 처리할 때 각 .then이 다음 실행할 콜백 함수를 promiseReactionJob으로 등록함.

이건 마이크로태스크 큐에 들어가고 현재 코드 실행이 끝나면 순차적으로 실행됨.

 

즉.

 

1.then()이 실행될 때 다음 단계 콜백을 큐에 넣고

2. 현재 스크립트가 끝나면

3.큐에 쌓인 콜백들이 순서대로 실행됨. 

 

이게 비동기지만 순차 실행되는 이유임. 

 

5. 그래서 등장한 게 async/await

같은 코드를 await으로 바꾸면 이렇게 됨: 

 

async function main() {
  try {
    const user = await fetchUser();
    const posts = await fetchPosts(user.id);
    const comments = await fetchComments(posts[0].id);
    const likes = await fetchLikes(comments[0].id);
    console.log(likes);
  } catch (e) {
    console.error(e);
  }
}

 

동기 코드처럼 자연스럽게 보이지만 하지만 내부적으로는 여전히 promise 체인과 같은 메커니즘을 가지고 있음. 

각 .then은 직전에 완료된  Promise의 결과를 받고 그 콜백이 마이크로태스크 큐에 들어가 순서대로 실행됨.

즉 앞 단계가 끝나야 다음 단계가 큐에 들어가고 실행됨. 

 

예시 : 

 

fetchUser() 완료
 → then #1 실행 (fetchPosts)
   → fetchPosts 완료
     → then #2 실행 (fetchComments)
       → fetchComments 완료
         → then #3 실행 (fetchLikes)
           → fetchLikes 완료
             → then #4 실행 (console.log)

 

 

'V8' 카테고리의 다른 글

CVE-2021-21220 - 2  (0) 2025.10.21
CVE-2021-21220 - 1  (0) 2025.10.21
V8 실행옵션  (0) 2025.10.09
addrof primitive  (4) 2025.08.24
SSA  (0) 2025.06.30