※ 비동기 처리를 사용하는 이유 - 특정 작업이 완료될 때까지 대기하지 않고 다음 작업을 바로 처리할 수 있도록 하기위해 사용한다. - Javascript는 싱글스레드 기반이기 때문에 한번에 하나의 작업만 처리를 할 수 있지만, 이런 비동기 처리를 사용하여 블로킹이 되지 않고 리소스를 보다 효율적으로 사용할 수 있게 해준다.
※ 동기 / 비동기 그림 예시 ※ 자바스크립트가 한번에 하나의 처리만 가능한 이유 - 자바스크립트 엔진에 하나의 호출 스택(콜 스택)밖에 없기 때문이다. ※ 호출 스택(콜 스택)이란? (mdn) - 자바스크립트 엔진에서 실행 컨텍스트가 쌓여 있는 스택으로, 현재 어떤 함수가 실행중인지, 그 함수 내에서 어떤 함수가 호출되어야 하는지, 등을 제어한다.
※ 실행 컨텍스트란? - scope, hoisting, this, closure등의 동작원리를 담고있는 가장 근본적인 개념이다. - 쉽게 말하면 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다. - 처음 스크립트를 실행했을때의 전역 실행 컨텍스트와 함수 호출 시 생성되는 함수 실행 컨텍스트로 나뉜다.
※ 호출 스택(콜 스택) 동작 원리 1. 전역 실행컨텍스트 최초 실행 2. 스크립트에서 특정 함수를 호출 3. 인터프리터는 해당 함수를 콜 스택에 추가
4. 해당 함수 안에서 호출되는 모든 함수를 콜 스택에 추가 5. 스택 구조(후입선출)로 가장 상위의 함수부터 실행하기 시작 6. 현재 함수가 끝나면, 인터프리터는 스택을 제거 7. 호출 스택 마지막 코드 목록에서 중단된 실행을 다시 시작 8. 스택이 할당된 공간보다 많은 공간을 차지하면, "stack overflow" 에러가 발생
※ 코드 + 그림으로 이해하는 콜 스택 동작 원리 시작!
전역 실행컨텍스트 최초 실행
firstFunction() 실행
firstFunction() 안에 있는 secondFunction() 실행
secondFunction()이 return과 동시에 콜 스택에서 빠짐
firstFunction()이 return과 동시에 콜 스택에서 빠짐 모든 코드의 실행이 끝나면서 전역 실행컨텍스트도 콜 스택에서 빠짐
Callback
※ Callback이란? - Callback은 다른 함수의 인자로 전달되는 함수이다. - 해당 함수는 고차 함수(HOC) 안에서 흐름에 따라 실행이 된다.
※ 사용 방법
function asyncFunction(callback) {
setTimeout(function () {
const result = "비동기 작업이 완료되었습니다!";
callback(null, result);
}, 2000);
}
console.log("비동기 작업 시작");
function handleResult(error, result) {
if (error) {
console.error('에러 발생:', error);
} else {
console.log(result);
console.log("비동기 작업 완료 후 다음 동작 수행");
}
}
asyncFunction(handleResult);
console.log("다음 동작 수행");
// 비동기 작업 시작 -> 비동기 작업이 완료되었습니다 -> 비동기 작업 완료 후 다음 동작 수행 -> 다음 동작 수행
※ 문제점 - 비동기 작업이 여러 단계를 거치거나 중첩된 경우 코드가 쉽게 읽기 어려워지고 유지보수가 어려워진다. - 이를 콜백 지옥이라고 부르며, 코드의 들여쓰기가 많아져 가독성이 떨어지는 문제가 발생된다.
Promise
※ Promise란? - Promise는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타내는 객체이다.
※ Promise 상태값 - 대기(pending) : 이행하지도, 거부하지도 않은 초기 상태 - 이행(fulfilled) : 연산이 성공적으로 완료된 상태 - 거부(rejected) : 연산이 실패한 상태
※ async-await란? - 비동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, Promise 객체를 반환한다. - async-await는 비동기 함수를 마치 동기 함수를 사용하듯이 작성할 수 있다. - 함수의 선언부에 async를 작성하고 내부에 실질적으로 비동기 처리를 하는 부분에 await를 추가해 주는 방식이다.
※ 사용 방법
function resolveAfter2Seconds() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result);
// Expected output: "resolved"
}
asyncCall();