자바스크립트에서는 많은 작업이 비동기로 이루어진다. 과거에는 이러한 비동기 작업을 콜백함수로 처리했지만, 애플리케이션의 규모가 커지면서 코드의 복잡도가 증가하고, 관리가 어려워지는 문제가 발생했다.
step1(function (value1) {
step2(function (value2) {
step3(function (value3) {
step4(function (value4) {
step5(function (value5) {
step6(function (value6) {
// Do something with value6
});
});
});
});
});
});
stepN 함수에 콜백이 계속 중첩하며 콜백 지옥이 생성되고, 이로인해 코드의 가독성이 떨어지고, 유지보수가 어려워진다.
try {
setTimeout(()=>{
throw new Error('Error');
}, 1000}
} catch {
console.error('에러가 캐치 될까요?', e);
}
에러 캐치가 안된다!
이는 비동기의 특성으로 인해 콜백함수가 이벤트 루프에 의해 다시 콜스택으로 돌아왔을 때, 이전에 감싸졌던 try ... catch 문
과 다른 실행 컨텍스트에서 실행되기 때문이다.
try {
setTimeout(() => {
try {
throw new Error("Error!");
} catch {
console.error("에러 발생!");
}
}, 1000);
} catch (e) {
console.error("캐치한 에러", e);
}
이렇게 콜백 함수 내부에서 감싸주면 에러 처리가 가능하지만, 벌써부터 코드의 가독성이 나빠져 눈살 찌뿌려지기 시작한다.
아무튼 이러한 문제를 해결하기 위해 나온 것이 Promise
이다.
Promise는 미래의 어떤 시점에 결과를 제공하겠다는 값의 대리자
역할을 한다. Promise는 비동기 작업의 성공 또는 실패를 표현하며, 다음 중 하나의 상태(state)를 가진다.
const promise = new Promise((resolve, reject) => {
if (비동기 처리 성공) {
resolve('result'); // fulfilled 상태로 result를 래핑해 반환
} else {
reject('failure reason'); // rejected 상태로 failure reason을 래핑해 반환
}
});
promise
.then(v => console.log(v)) // fulfilled 상태일 때 실행
.catch(e => console.error(e)) // rejected 상태일 때 실행
.finally(() => console.log('finally')); // 상태와 상관없이 항상 실행
Promise는 then, catch, finally 메서드를 이용하여 비동기 작업을 순차적으로 처리할 수 있으며, 이를 프로미스 체이닝
이라고 한다.
Promise.resolve()
.then(() => { /* 작업 1 */ })
.then(() => { /* 작업 2 */ })
.then(() => { /* 작업 3 */ })
.catch(() => { /* 에러 처리 */ })
.finally(() => { /* 마무리 작업 */ });
위에 있던 콜백 지옥과 비교해보면 가독성이 매우 좋아 보인다.
Promise의 후속 처리 메서드(then, catch, finally)의 콜백 함수는 마이크로태스크 큐
에 저장되며, 일반 태스크 큐 보다 우선적으로 실행된다.
https://velog.io/@kgh7427_/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84Event-loop
setTimeout(() => {
console.log("1");
}, 0);
Promise.resolve()
.then(() => {
console.log("2");
})
.then(() => {
console.log("3");
});
.then
블록이 실행된다..catch
블록으로 이동한다.function makePayment(url, shouldSucceed) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldSucceed) {
resolve(`송금 성공: ${url}`);
} else {
reject(new Error(`송금 실패: ${url}`));
}
}, 1000); // 1초 후에 결과 반환
});
}
const p1 = makePayment("url1", true); // 성공
const p2 = makePayment("url2", false); // 실패
const p3 = makePayment("url3", true); // 성공
const p4 = makePayment("url4", false); // 실패
const p5 = makePayment("url5", true); // 성공
Promise.all([p1, p2, p3, p4, p5])
.then((results) => {
console.log("all :", results);
console.log("모든 송금이 성공적으로 완료되었습니다.");
})
.catch((error) => {
console.error("송금 중 오류가 발생했습니다.", error);
});
function makePayment(url, shouldSucceed) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldSucceed) {
resolve(`송금 성공: ${url}`);
} else {
reject(new Error(`송금 실패: ${url}`));
}
}, 1000); // 1초 후에 결과 반환
});
}
const p1 = makePayment("url1", true); // 성공
const p2 = makePayment("url2", false); // 실패
const p3 = makePayment("url3", true); // 성공
const p4 = makePayment("url4", false); // 실패
const p5 = makePayment("url5", true); // 성공
Promise.allSettled([p1, p2, p3, p4, p5]).then((results) => {
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log(result.value);
} else {
console.error(result.reason.message);
// 실패한 송금에 대해 재시도 로직을 추가할 수 있습니다.
}
});
});
async/await는 Promise를 더 쉽게 사용하기 위한 문법 설탕이다. 개인적으로 느끼기엔 병렬처리를 할 때는 Promise가 오히려 직관적이다.
const p1 = fetch('url1');
const p2 = fetch('url2');
const p3 = fetch('url3');
Promise.all([p1, p2, p3]).then((results) => {
console.log(results);
});
async/await를 사용해서 병렬처리를 할 때는 뭔가 빙 돌아가는 느낌이 든다.
async function fetchInParallel() {
const p1 = fetch('url1');
const p2 = fetch('url2');
const p3 = fetch('url3');
const [data1, data2, data3] = await Promise.all([p1, p2, p3]);
console.log(data1, data2, data3);
}
async로 함수를 묶어주고 병렬처리할 때 Promise를 어차피 써야한다. 이럴꺼면 그냥 처음부터 Promise를 사용하는게 낫다고 생각된다.
https://www.youtube.com/watch?v=0f-jNhnN0Qc&list=PLcqDmjxt30Rt9wmSlw1u6sBYr-aZmpNB3&index=11
https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/main/JavaScript#promise
https://velog.io/@seul06/JavaScript-콜백-지옥
https://programmingsummaries.tistory.com/325
모던 자바스크립트 Deep Dive
https://poiemaweb.com/es6-promise