Skip to content

[week6_JISUNG] 45장, 46장 퀴즈#15

Open
jstar000 wants to merge 1 commit intomainfrom
week6_JISUNG
Open

[week6_JISUNG] 45장, 46장 퀴즈#15
jstar000 wants to merge 1 commit intomainfrom
week6_JISUNG

Conversation

@jstar000
Copy link
Member

@jstar000 jstar000 commented Dec 2, 2025

Q1. 비동기 함수와 콜백

function foo() {
	let todos;
	
	const get = (url) => {
		const xhr = new XMLHttpRequest();
		xhr.open('GET', url);
		xhr.send();
		
		xhr.onload = () => {
			if (xhr.status === 200) {
				**console.log("A: 비동기 콜백 (함수 내부)");**
			} else {
				console.error();
			}
		}
	}
	
	get('url');
	console.log(todos); // undefined
	**console.log("B: 함수 내부 동기 코드");**
}

foo(); // foo 함수 호출
**console.log("C: 함수 외부 동기 코드");**

Q: 위 코드의 실행 결과는?

Q2. 비동기 함수와 태스크 큐

console.log('시작');

setTimeout(() => {
  console.log('A');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('C');
    setTimeout(() => console.log('D'), 0);
  })
  .then(() => console.log('E'));

console.log('끝');

Q: 이 코드의 실행 순서는?

Q3. async/await

Q: 콜백을 인수로 받는 비동기 함수는 try-catch로 에러 처리가 불가능한데, async/await는 왜 가능한가요?

@jstar000
Copy link
Member Author

jstar000 commented Dec 2, 2025

Q1.

B → C → A

  1. foo 함수 호출

  2. foo 함수 내부 코드 평가 및 실행

    todos, get이 foo 함수의 lexical environment에 식별자로 등록됨, …

    const get = (url) => { ... }; ← get에 함수 객체 할당
    get('url') ← get 함수 호출

  3. get 함수 내부 코드 평가 및 실행

    xhr.send(); ← 비동기 요청 시작
    xhr.onload = () ⇒ { } ← xhr 인스턴스에 핸들러만 등록(핸들러의 등록은 동기적으로 발생, 할당된 핸들러(콜백 함수)의 실행은 비동기적으로 발생)

  4. get 함수가 콜 스택에서 pop된 뒤에, foo 함수 코드 계속 실행

    console.log(todos); // undefined ← 콜 스택에 푸시
    ****console.log("B: 함수 내부 동기 코드"); ← 콜 스택에 푸시

이때,

  • get 함수가 종료되기 전
  • get 함수가 종료된 후 console.log()가 실행되고 있을 때

이 시점에 서버로부터의 응답이 도착할 수도 있음. 응답이 도착하는 즉시 브라우저의 Web API는 onload 콜백 함수를 태스크 큐에 등록함.

하지만 아직 example 함수 내부에 실행되지 않은 동기 코드들이 남아있고, 함수 내부에서 콜스택에 들어갈 수 있는 동기 코드가 남아있을 때는 task queue에 등록된 비동기 함수들은 절대로 먼저 콜스택에 들어갈 수 없으므로 → load 함수의 콜백함수는 무조건 모든 console.log()들이 다 실행돼 콜 스택에서 pop됐을 때 이벤트 루프에 의해 콜 스택에 등록돼 실행됨

Q: foo 함수가 끝난 후, 그 뒤에 있는 "외부 동기 코드"보다 foo 함수에 의해 태스크 큐에 등록된 비동기 함수가 먼저 처리될까?
A: 외부 동기 코드가 항상 먼저 실행됨.

이벤트 루프는 특정 함수의 종료 여부가 아닌, 전역 스코프를 포함한 현재 실행 중인 모든 동기 코드가 완료되기를 기다림. 함수 스코프는 중요하지 않다!

이벤트 루프는 C까지 모두 실행되어 콜 스택이 완전히 비워진 후에야 태스크 큐에 있는 A를 처리함

Q2.

  1. 동기 코드부터 먼저 실행
  2. 각 매크로태스크 후에 마이크로태스크 큐를 전부 비운다
  3. Promise 체인은 순서대로 연결된다
  1. console.log(시작) 실행 → ‘시작’ 출력

  2. setTimeout(..., 0) 만남

    • setTimeout 호출 → 콜백이 Web API의 타이머에 등록됨
    • 타이머 만료 시 태스크 큐로 이동
    • 콜 스택이 비었을 때 이벤트 루프에 의해 콜 스택으로 이동해 콜백 실행

    ⇒ 여기서는 타이머가 0초로 세팅됐으므로 콜백이 Web API에 등록되자마자 바로 매크로태스크 큐로 이동

  3. Promise.resolve()... 만남

    .then({ C ... }) 메서드의 콜백 함수는 즉시 실행되지 않고, 마이크로태스크 큐에 등록됨

    .then( E ... )도 마이크로태스크 큐에 등록됨(C 콜백 뒤)

  4. console.log(끝) 실행 → ‘끝’ 출력

이제 함수 내의 모든 동기코드가 완료돼 콜 스택이 비었다!

  • 현재 Task Queue 상황
    • Macro Task Queue

      [ setTimeout(() => { console.log('A'); ... }, 0) ]

    • Micro Task Queue

      [ .then(() => { console.log('C'); ... }), .then(() => console.log('E')) ]

이벤트 루프는 마이크로태스크 큐를 태스크 큐보다 먼저, 그리고 완전히 비울 때까지 처리함

  1. 이벤트 루프가 마이크로태스크 큐의 첫 번째 콜백을 콜 스택으로 옮김

    5-1. ‘C’ 출력

    5-2. setTimeout(() => console.log('D'), 0)가 실행되며, 이 콜백이 태스크 큐의 맨 뒤로 이동

  2. 마이크로태스크 큐가 아직 비어있지 않으므로 처리 계속

    .then(() => console.log('E')) 가 콜 스택으로 이동, ‘E’ 출력

이제 마이크로태스크큐가 텅 비었다!

  • 현재 Task Queue 상황
    • Macro Task Queue

      [ setTimeout(() => { console.log('A'); ... }, 0), setTimeout(() => console.log('D'), 0) ]

    • Micro Task Queue

      [ ]

  1. Macro Task Queue에 있는 두 작업이 순차적으로 콜 스택으로 이동하며 실행됨

정답: 시작 → 끝 → C → E → A → D

Q3.

  • 콜백 기반

    try {
      setTimeout(() => {
        throw new Error('Error!');
      }, 1000);
      // <- try 블록 끝남
    } catch (e) {
      // 여기는 이미 끝났음
    }
    1. try 블록 진입

    2. setTimeout 호출 (콜백만 등록)

    3. try 블록 완전히 끝남

    4. catch 블록도 끝남

    5. 1초 경과

    6. 콜백 실행 → 에러 발생 → 하지만 try-catch는 이미 끝났으므로 처리 안 됨

    try-catch 실행 컨텍스트가 이미 종료된 상태에서 콜백이 실행되기 때문에, 에러가 발생해도 처리할 catch 블록이 없다!

  • async/await

    async function test() {
      try {
        const result = await promise ~~ ;  // <- 여기서 대기
        console.log(result);
      } catch (e) {
        console.error(e);  // 에러 정상 처리
      }
    }
    1. async 함수 실행 시작
    2. try 블록 진입
    3. await promise 만남
      → await 키워드는 프로미스가 settled 상태(비동기 처리가 수행된 상태)가 될 때까지 대기하다가 settled 상태가 되면 프로미스의 결과를 반환함
      → try 블록은 여전히 활성 상태
    4. Promise가 reject됨
    5. await가 그 에러를 받음 → 같은 실행 컨텍스트에서 catch 블록으로 이동
    6. catch 블록 실행 → 에러 처리됨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant