웹 애플리케이션에서는 네트워크 요청 이벤트 처리 타이머와 같이 멀티로 처리해야 하는 경우가 많다.
Browser 내부의 Multi Thread인
Web APIs에서 비동기 + 논블로킹으로 처리된다.
비동기 + 논블로킹은 Main Thread가 작업을 다른 곳에 요청하여 대신 실행하고, 그 작업이 완료되면 Event나 Callback 함수를 받아 결과를 실행하는 방식을 말한다.
브라우저의 동작 타이밍을 제어하는 관리자

구성 요소 :
Web APIs : 브라우저에서 제공하는 API 모음, 비동기적으로 실행되는 작업들을 전담하여 처리Event Table : Callback Queue : 비동기적 작업이 오나료되면 실행되는 함수들이 대기하는 공간Call Stack : Javascript 엔진이 코드 실행을 위해 사용하는 메모리 구조Event Loop : 비동기 함수들을 적절한 시점에 실행시키는 관리자
setTimeout, setInterval, fetch, addEventListener와 같이 비동기로 처리되는 함수들의 callback 함수가 들어가는 Queuepromise.then, process.nextTick, MutationObserver와 같이 우선적으로 비동기로 처리되는 함수들의 callback 함수가 들어가는 Queue 가장 우선순위가 높은 microtask queue를 먼저 처리하여 비우고 그 다음 task queue의 콜백을 처리한다.
ex) Promise.then과 setTimeout중 무엇이 먼저 실행될까?
Promise.then은 microtask queue
setTimeout은 macrotask queue
=> Promise.then이 먼저 실행된다.
Event Loop는 비동기 함수 작업을 Web API에 옮기는 역할을 하고 작업이 완료되면 Callback Queue에 적재했다가 다시 JavaScript 엔진에 적재해 수행시키는 일종의 작업을 옮기는 역할만 한다.
작업을 처리하는 주체는 Javascript 엔진과 Web API다.
그래서 Event Loop는
1. Call Stack에 현재 실행중인 작업이 있는지
2. Task Queue에 대기중인 작업이 있는지
반복적으로 확인하는 일종의 무한루프만 돌고,
대기 작업이 있따면 작업을 옮겨주는 형태로 동작한다.
console.log('Start!'); // 1
setTimeout(() => { // 3
console.log('Timeout!');
}, 0);
Promise.resolve('Promise!').then(res => console.log(res)); // 4
console.log('End!'); // 2

console.log('Start!'); 적재되고 실행되어 출력한다.setTimeout 코드가 Call Stack에 적재되고 실행되면, () => { console.log('Timeout!') }이 Event Loop에 의해 Web API로 옮겨지고 타이머가 작동한다.setTimeout의 callback 함수는 Event Loop에 의해 MacroTask Queue에 적재한다.Promise 코드가 Call Stack에 적재되어 실행되고, then 핸들러 callback 함수가 Event Loop에 의해 MicroTask Queue에 적재한다.console.log('End') 코드가 실행되어 출력한다.MicroTask Queue에 남아있는 callback이 우선적으로 처리된다.MicroTask Queue가 비어지면, MacroTask Queue에 있는 callback 함수를 Call Stack에 적재해 실행한다.Start!
End!
Promise!
Timeout!
Async/Await는 비동기 논블로킹 동작을 동기적으로 처리하기 위해 복잡한 콜백이나then핸들러의 지옥 코드를 극복하는 핵심이다.
Async/Await는 단순히 비동기를 동기적으로 처리해준다는 효과만 알고있어서, 비동기 코드와 동기 코드가 같이 쓰여져 있을 경우 이들의 확실한 처리 과정을 알아보자
async 함수를 동일 코드 레벨에서 실행해보자.
const one = () => Promise.resolve('One!');
async function myFunc(){
console.log('In function!');
const res = await One();
console.log(res);
}
console.log('Before Function!');
myFunc();
console.log('After Function!');

Before Function이 출력된다.MyFunc()이 호출된다.one() 비동기 함수를 호출한다.one() 비동기 함수 왼쪽에 await 키워드로 인해, MyFunc 함수의 내부 실행은 잠시 중단되고 Call Stack에서 빠져나와 나머지 부분은 MicroTask Queue에 적재된다.await 키워드를 인식하면 async 함수의 실행은 지연되는 것으로 처리하기 때문이다.Before function!
In function!
After function!
One!
이번에는 MyFunc() 함수를 호출하는 Main 스택에서 await 키워드를 붙여주면 어떻게 될까?
const one = () => Promise.resolve('One!');
async function myFunc(){
console.log('In function!');
const res = await one();
console.log(res);
}
console.log('Before Function!');
await myFunc(); // await를 추가했다.
console.log('After Function!');
Before Function!
In function!
One!
After Function!
이전 결과와 다르다.
const one = () => Promise.resolve('One!');
async function myFunc(){
console.log('In function!');
const res = await one();
console.log(res);
}
console.log('Before Function!');
await myFunc();
console.log('After Function!');
/* ---------------- ↓↓↓ 변환 ↓↓↓ ---------------- */
const one = () => Promise.resolve('One!');
function myFunc(){
console.log('In function!');
return one().then(res => { // await -> then
console.log(res);
});
}
console.log('Before Function!');
myFunc().then(() => { // await -> then
console.log('After Function!');
});
즉, await myFunc() 다음에 나오는 코드들이 myFunc()의 then 핸들러의 콜백으로서 Microtask Queue에 적재되고 이벤트 루프에 다시 Call stack에 옮겨져서 실행되는 것이다.