Intro
...잠깐 봤는데도 이해가 안되네 resolve 에 도달하면 then이고 reject 도달하면 catch? 개발자 의도나 실수로 둘 다 도달하지 않으면 일단 코드는 정상적으로 동작한거니 then으로 가나?.... 어지럽네 진짜
Promise
MDN Promise 문서에 따르면 아래와 같이 나와있다.
Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.
Promise는 프로미스가 생성된 시점에는 알려지지 않았을 수도 있는 값을 위한 대리자로, 비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 처리기를 연결할 수 있습니다. 프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하는 것이 아니고, 미래의 어떤 시점에 결과를 제공하겠다는 '프로미스(promise)'를 반환합니다.
쉽게 설명하면 비동기 작업을 진행하고 처리 결과를 reslove()와 reject() 함수를 사용해서 fulfilled 또는 rejected 로 상태로 반환하는 객체이다.
특징
- 최초의 상태(Pending)에서 상태가 변경되면 다시 변경되지 않는다.
- Fulfilled 또는 Rejected 상태에서 resolve() 와 reject()함수를 만나면 무시한다.
- resove() 또는 reject() 함수를 만나도 바로 반환하는게 아니라 그 이후에 있는 코드들도 정상적으로 실행이된다.
- 한번 실행된 Promise 객체는 다시 실행시켜도 내부 로직이 동작하지 않는다. => 함수로 감싸서 실행하면 재실행 가능
- 체이닝을 이용하여 여러 비동기 작업을 순차적으로 실행 가능
3가지 상태(states)
- Pending(대기) : Promise 실행 시 초기 상태이며, 아직 성공이나 실패가 되지 않은 상태, new Promise()
- Fulfilled(이행) : 작업이 완료되어 결과를 반환한 상태, resolve()
- Rejected(실패) : 비동기 작업이 실패하거나 오류가 발생한 상태, reject()
Promise 기본 문법 및 예제
기본 문법
const <Promise 객체> = new Promise((resolve, reject) => {
resolve(); // Promise 성공
// OR
reject(); // Promise 실패
});
<Promise 객체>.then(<콜백 함수>); // fulfilled 상태
// 또는
<Promise 객체>.catch(<콜백 함수>); // rejected 상태
사용 예제
const myPromise = new Promise((resolve, reject) => {
const num = Math.random();
if (num > 0.5) {
resolve("성공 : " + num);
} else {
reject("실패 : " + num);
}
});
myPromise
.then((success) => console.log("then : ", success))
.catch((error) => console.log("catch : ", error))
.finally(() => console.log("finally 실행");
후속 처리 메소드
.then()
- 최대 2개까지 인자(콜백 함수)를 받을 수 있다.(onFulfilled, onRejected)
- 첫 번째 인자는 Promise 코드에서 resolve()가 호출되어 fulfilled 상태일때 실행
- 두 번째 인자는 Promise 코드에서 reject()가 호출되거나 에러 또는 예외처리가 발생하여 rejected 상태일때 실행
- 기본적으로 반환되는 값은 Promise 객체(상태 : fulfilled)
- 내부 코드 실행 도중에 예외가 발생하면 rejected 상태로 변경
.catch()
- reject()가 호출되거나 에러 또는 예외처리가 발생하는 등 Promise 상태가 rejected 상태일 때 실행
- 기본적으로 반환되는 값은 Promise 객체(상태 : fulfilled)
.finally()
- Promise의 상태가 fulfilled인지 rejected인지 상관 없이 항상 실행
- 예외가 발생하지 않는 한 받은 Promise 상태를 그대로 반환
- 반환되는 값은 Promise 객체
결론적으로, Promise Chaining이 가능하도록 .then(), .catch(), .finally()는 항상 새로운 Promise를 반환합니다!
정적 메서드
.all()
- 인자로 배열에 여러개의 Promise 객체를 담아서 한번에 반환 받는다.
- 하나라도 거부되면 즉시 전체가 거부 됩니다.
Promise.all([Promise 객체 1, Promise 객체 2, Promise 객체 3, ...])
.then((results) => {
console.log("전부 성공");
results.forEach((result, num) => {
console.log(`${num}번쨰 Promise 결과 : ${result}`);
})
.catch(() => console.log("하나 이상 실패");
.allSettled()
- .all()과 달리 거 rejected 되는 것이 있더라도 모든 Promise가 완료될 때까지 기다린다.
Promise.allSettled([Promise 객체 1, Promise 객체 2, Promise 객체 3, ...])
.then((results) => {
console.log("모든 Promise가 완료됨");
results.forEach((result, num) => {
if(result.status == 'fulfilled') {
console.log(`${num}번째 Promise 상태: ${result.status}, 결과: ${result.value}`);
} else if (result.status == 'rejected') {
console.log(`${num}번째 Promise 상태: ${result.status}, 이유: ${result.reason}`);
}
});
});
.race()
- 입력 받은 여러 개의 Promise 객체들중 가장 먼저 완료된 결과만 반환
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('promise1'), 100);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('promise2'), 200);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => resolve('promise2'), 200);
});
Promise.race([promise1, promise2, Promise3])
.then((result) => console.log(`가장 빨리 완료한 Promise는 ${result}`));
.resolve() 와 .reject()
- 들어오는 입력값을 fulfilled, rejected 상태인 Promise 객체로 감싸서 반환
const resolvePromise = Promise.resolve(value) // 상태가 Fulfilled인 Promise 객체 반환
resolvePromise
.then((result) => console.log(result)) // 출력 : value
const rejectPromise = Promise.reject(value) // 상태가 Rejected인 Promise 객체 반환
rejectPromise
.catch((error) => console.log(error)) // 출력 : value
Async/Await
Promise 의 단점을 보완하여 좀 더 간결하고 직관적으로 작성할 수 있도록 ES8부터 나온 기능으로 비동기 코드를 동기처럼 작성할 수 있다.
말그대로 Async 비동기 라는 뜻으로 함수 앞에 붙어서 비동기 함수라는걸 뜻하고, Await 기다리다 라는 뜻으로 Promise 함수가 결과값을 반환하기 전까지 그 이후 코드들을 진행시키지 않고 기다린다.
- Async : 비동기로 동작시키고 싶은 함수 앞에 위치하며, Promise 객체를 반환한다.
- Await : Async 함수 내에서 만 사용할 수 있으며, Promise를 반환하는 함수 앞에 붙여서 동작한다. (Promise가 아니라도 붙일수는 있지만 기대대로 동작하지 않음)
기본 문법
async function 함수명() {
await 비동기_메서드();
}
기존 Promise vs Async/Await
// 기존 Promise 문법
function fetchData(url) {
return fetch(url)
.then((result) => {
console.log("코드 동장 성공 : ", result);
})
.catch((error) => {
console.error("에러 발생 : ", error);
});
}
// Async/Await 사용 코드
async function fetchData(url) {
try {
const result = await fetch(url);
console.log("코드 동작 성공 : ", result);
} catch (error) {
console.error("에러 발생 : ", error);
}
}
병렬처리
1. 기본적인 방식
async function fetchData() {
console.log("데이터 요청 시작");
const response1 = await promise1;
console.log(response1);
const response2 = await promise2;
console.log(response2);
}
// 내가 생각했을 때 조금 더 좋은 방법
async function fetchData() {
const response1 = promise1;
const response2 = promise2;
const data1 = await response1;
const data2 = await response2;
console.log(response1);
console.log(response2);
}
2. Promise.all 활용 (로직은 조금 차이나지만 allSettled도 가능)
async function fetchData() {
const [response1, response2] = await Promise.all([promise1, promise2]);
console.log(response1);
console.log(response2);
}
마무리
Promise 길어도 오전이면 얼추 다 찾아보겟지 라는 생각을 가지고 시작했는데, 오전은 커녕 하루종일 찾아보면 찾아볼수록 계속 먼가가 나온다... 나 자바도 이렇게 까지 파본적 있던가? 이쯤되니 난중에 자바 공부 다시 시작해서 하나하나 뜯어보면 또 몰랐던 먼가가 나올거 같네...
그래도 Async/Await은 동작은 비동기이지만 동기 코드 처럼 보여서 가독성이라던가 try-catch 등으로 예외처리가 편해진거 같다.
[참고]
'Frondend > JavaScript' 카테고리의 다른 글
[JavaScript] 프로토타입(Prototype) 과 클래스(Class) (0) | 2025.02.12 |
---|---|
[JavaScript] Fetch API (1) | 2025.02.09 |
[JavaScript] 비동기와 타이머 (w. Event Loop) (0) | 2025.02.06 |
[JavaScript] 호이스팅(Hoisting) (0) | 2025.02.06 |
[JavaScript] this 알아보자 (1) | 2025.02.05 |