🌐 1. JavaScript의 비동기 처리 이해하기
JavaScript는 싱글 스레드로 동작해요. 즉, 한 번에 하나의 작업만 처리할 수 있다는 뜻이죠. 그런데도 여러 비동기 작업을 동시에 처리하는 것처럼 보이는 이유는 JavaScript의 이벤트 루프(Event Loop) 덕분이에요. 자, 이 과정이 어떻게 이루어지는지 차근차근 설명해보겠습니다.
🎯 JavaScript는 싱글 스레드인데 비동기 작업을 어떻게 처리할까요?
JavaScript는 비동기 작업을 처리하기 위해 이벤트 루프와 태스크 큐라는 개념을 사용합니다.
- 비동기 함수 호출: 비동기 함수(예:
setTimeout
,fetch
,async/await
)가 호출되면, 그 작업은 브라우저나 Node.js의 백그라운드에서 처리돼고, 이때 JavaScript는 다른 코드를 계속해서 실행할 수 있습니다.
console.log("Start");
setTimeout(() => {
console.log("Inside setTimeout");
}, 2000);
console.log("End");
위 코드에서는 setTimeout
이 호출되었지만, JavaScript는 이 작업을 백그라운드에서 처리하고 console.log("End")
를 먼저 실행한 후, 2초 후에 비동기 작업이 완료되면 "Inside setTimeout"
이 출력됩니다.
- 태스크 큐(Task Queue): 비동기 작업이 완료되면, 그 결과는 태스크 큐에 들어갑니다.
setTimeout(() => {
console.log("First task");
}, 1000);
setTimeout(() => {
console.log("Second task");
}, 500);
console.log("Synchronous task");
이 예시에서는 500ms
후에 "Second task"
가, 1000ms
후에 "First task"
가 출력되는데, 그 전에 "Synchronous task"
가 먼저 출력돼요. 이처럼 태스크 큐는 작업이 완료된 순서대로 큐에 저장됩니다.
- 이벤트 루프(Event Loop): 이벤트 루프는 콜 스택이 비어 있을 때, 태스크 큐에 있는 작업을 콜 스택으로 옮겨와 실행합니다.
🎯 비동기 함수 내부에서 await
를 사용하면 콜 스택과의 관계는 어떻게 되나요?
await
는 비동기 함수 내에서 특정 작업이 완료될 때까지 기다리도록 만드는 키워드입니다.
await
를 만나면 JavaScript는 해당 비동기 작업이 완료될 때까지 기다리지 않고, 함수의 실행을 일시 중지합니다.- 이 동안, 다른 코드가 실행될 수 있도록 콜 스택을 비워둡니다.
- 비동기 작업이 완료되면, 그 작업은 태스크 큐에 들어가고, 이벤트 루프가 이 태스크를 콜 스택으로 옮겨 실행을 재개합니다.
async function fetchData() {
console.log("Fetching data...");
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log("Data received:", data);
}
console.log("Before fetch");
fetchData();
console.log("After fetch");
이 코드에서는 fetchData
함수 내에서 await
가 호출될 때 JavaScript는 일시적으로 함수 실행을 중지하고, 나머지 코드 (console.log("After fetch")
)를 먼저 실행해요. 이후, 데이터가 받아지면 "Data received:"
와 함께 출력돼요.
🎯 비동기 작업이 완료된 후 태스크 큐로 결과가 전달되는 과정은 어떻게 이루어지나요?
비동기 작업이 완료되면, 그 결과는 태스크 큐에 저장돼요. 이 과정은 다음과 같습니다.
- 백그라운드 작업 완료: 비동기 함수로 백그라운드에서 처리된 작업이 완료되면, 그 결과는 태스크 큐에 들어갑니다.
- 이벤트 루프: 이벤트 루프는 콜 스택이 비었을 때 태스크 큐에서 작업을 가져와서 콜 스택으로 옮겨옵니다.
- 작업 실행: 이 태스크는 콜 스택에서 실행되고, 그 결과가 반영됩니다.
console.log("Start");
setTimeout(() => {
console.log("First task");
}, 1000);
setTimeout(() => {
console.log("Second task");
}, 500);
console.log("End");
이 코드에서는 "Second task"
가 태스크 큐에 먼저 들어가고, 그 다음 "First task"
가 들어갑니다. 이벤트 루프는 콜 스택이 비었을 때 태스크 큐에서 "Second task"
를 먼저 가져와 실행합니다.
🎯 비동기 작업과 이벤트 루프의 역할
비동기 작업이 수행될 때, JavaScript는 해당 작업을 브라우저의 백그라운드로 넘기고, 다른 작업을 계속 수행할 수 있습니다. 이 과정에서 이벤트 루프는 다음과 같은 중요한 역할을 합니다:
- 백그라운드에서 작업 처리: 비동기 작업이 백그라운드에서 완료되면, 그 결과는 태스크 큐에 전달됩니다.
- 이벤트 루프가 주도권을 가져옴: 이벤트 루프는 콜 스택이 비었을 때, 태스크 큐에 있는 작업을 콜 스택으로 가져와 실행해합니다. 이때 비동기 작업의 결과도 처리됩니다.
- 결과 처리: 비동기 작업이 완료된 후, 이벤트 루프가 이를 JavaScript 엔진으로 돌려줘서 후속 작업을 이어가게 합니다.
🎯 mounted
훅에서 여러 비동기 작업이 순차적으로 실행되지 않는 이유는 무엇인가요?
mounted
훅에서 비동기 작업을 실행하면, 이 작업들은 병렬로 실행됩니다. 이는 각 비동기 작업이 별도로 시작되기 때문입니다. 만약 순차적으로 실행하고 싶다면, await
키워드를 사용하거나 Promise
체이닝을 해야 합니다.
async mounted() {
await this.fetchData1();
await this.fetchData2();
await this.fetchData3();
}
위 코드처럼 await
를 사용하면, 첫 번째 작업이 끝날 때까지 기다린 후 다음 작업이 실행됩니다. 하지만 await
를 사용하지 않으면 모든 작업이 동시에 시작됩니다.
mounted() {
this.fetchData1();
this.fetchData2();
this.fetchData3();
}
이 경우, fetchData1
, fetchData2
, fetchData3
는 동시에 호출되고 병렬로 실행됩니다.
🎯 콜 스택, 태스크 큐, 마이크로태스크 큐 모두 하나의 스레드에서 처리가 가능한가요?
맞아요! 콜 스택, 태스크 큐, 그리고 마이크로태스크 큐는 모두 하나의 스레드에서 처리됩니다. JavaScript는 싱글 스레드 언어이기 때문에, 이 모든 작업이 한 줄로 정리된 하나의 스레드에서 실행됩니다.
🎯 태스크 큐와 마이크로태스크 큐도 결과를 저장하는 곳인가요?
엄밀히 말하면, 태스크 큐와 마이크로태스크 큐는 결과를 저장하는 곳이라기보다는, 실행 대기 중인 작업들을 보관하는 곳입니다. 비동기 작업이 완료되면 이 큐에 작업이 들어가고, 이벤트 루프가 이를 콜 스택으로 가져와 실행합니다.
Promise.resolve().then(() => {
console.log("Microtask 1");
});
setTimeout(() => {
console.log("Task");
}, 0);
Promise.resolve().then(() => {
console.log("Microtask 2");
});
console.log("Synchronous task");
이 예시에서 "Microtask 1"
과 "Microtask 2"
는 마이크로태스크 큐에 들어가고, "Task"
는 태스크 큐에 들어가요. 콜 스택이 비면 마이크로태스크가 먼저 실행되고, 그 다음 태스크가 실행합니다.
🎯 태스크 큐는 선입선출, 콜 스택은 후입선출 방식으로 동작하나요?
네, 맞아요!
- 태스크 큐는 선입선출(FIFO) 방식으로 동작해요. 먼저 들어온 작업이 먼저 실행됩니다.
- 콜 스택은 후입선출(LIFO) 방식으로 동작해요. 마지막에 들어온 작업이 먼저 실행됩니다.
function example() {
function inner() {
console.log("Inner function");
}
console.log("Before inner");
inner();
console.log("After inner");
}
example();
위 코드에서 example()
이 호출되면, example
함수가 콜 스택에 쌓이고, 그 안에서 inner()
가 호출되면 inner
함수가 콜 스택에 추가고, inner
함수가 실행된 후 스택에서 빠지고, 그 다음 example
함수가 완료됩니다.
🎯 mounted
에서 여러 함수를 호출할 때, 함수들이 콜 스택에 한 번에 쌓이는 게 아니라 하나씩 처리 후 다시 쌓이나요? 함수 내에 함수가 있을 때만 스택에 넣나요?
mounted
에서 여러 함수를 호출하면, 각 함수 호출이 순차적으로 콜 스택에 쌓이고 하나씩 처리됩. 함수 내에 또 다른 함수가 있다면, 그 함수가 콜 스택에 추가로 쌓이게 됩니다.
function outer() {
console.log("Outer function start");
function inner() {
console.log("Inner function");
}
inner();
console.log("Outer function end");
}
outer();
이 예시에서 outer()
가 호출되면, outer
함수가 콜 스택에 쌓이고, 그 안에서 inner()
가 호출되면 inner
함수가 콜 스택에 쌓입니다. inner
가 먼저 실행되고, 완료되면 스택에서 빠지고, 그 다음 outer
함수가 완료됩니다.
📌 요약
- JavaScript는 싱글 스레드지만, 이벤트 루프와 태스크 큐를 이용해 비동기 작업을 효율적으로 처리한다.
await
를 사용하면 콜 스택이 비동기 작업이 완료될 때까지 기다리지 않고 비워질 수 있다.- 비동기 작업이 완료되면 결과는 태스크 큐에 저장되고, 이벤트 루프가 이를 처리한다.
- 이벤트 루프는 비동기 작업의 완료를 감지하고, 그 결과를 콜 스택에 전달한다.
mounted
에서 여러 비동기 작업을 순차적으로 실행하려면await
를 사용해야 한다.- 콜 스택, 태스크 큐, 마이크로태스크 큐는 모두 하나의 스레드에서 처리된다.
- 태스크 큐는 선입선출, 콜 스택은 후입선출 방식으로 동작한다.
'Java Script' 카테고리의 다른 글
[JavaScript] Array에서 특정 데이터 개수 구하기 #reduce (0) | 2024.08.12 |
---|---|
[Java Script ]new Map() 형식 forEach 하기 (0) | 2023.03.30 |
[Java Script]map, filter, reduce 동작 원리 알아보기 (2) | 2023.03.29 |
[Java Script] textarea 글자 수 제한, 엔터키 입력 제한 (0) | 2022.01.06 |