Promise 객체 란?
- 어떤 작업에 관한 '상태 정보'를 갖고 있는 객체입니다.
- 작업의 결과가 promise 객체에 저장됩니다. promise 객체를 보면 작업이 성공했는지, 실패했는지 알 수 있습니다.
promise 객체의 3가지 상태
- promise 객체는 3가지 중 1개의 상태를 가집니다.
- 작업이 진행중임을 의미하는 pending.
- 작업이 성공적으로 완료되었음을 의미하는 fulfilled. 이때, 작업이 성공해서 promise 객체가 fulfilled 상태가 되면, promise 객체는 '작업의 성공 결과'도 함께 갖게됩니다. (작업 성공 결과)
- 작업이 실패했음을 의미하는 rejected. 이때, 작업이 실패해서 promise 객체가 rejected 상태가 되면, promise 객체는 '작업의 실패 이유'에 관한 정보도 함께 갖게됩니다. (작업 실패 정보)
- 참고로, pending 상태에서 한번 fulfilled 상태 또는 rejected 상태가 되면 다시 다른 상태를 가질 수 없습니다.
왜 사용할까?
- 비동기 작업을 순차적으로 실행하기 위해서 사용합니다.
- callback 함수가 많아지면 속칭, callback 지옥에서 빠져 가독성이 매우 낮아집니다. callback 함수를 보기좋게 하기위해서 Promise를 사용합니다.
- 그러나, 비동기적 처리가 가능하게 바꿔주는 문법이 아닌, 단지 callback 함수 대신해서 보기 쉽게 만들어주는 것이라는 사실을 잊어서는 안 됩니다.
promise의 메서드
- promise는 then, catch, finally 3가지 메서드를 가집니다.
- then: promise 객체가 fulfilled 상태가 되면 실행할 콜백함수를 등록하는 메서드 입니다.
- catch: promise 객체가 rejected 상태가 되면 실행할 콜백함수를 등록하는 메서드 입니다.
- finally: 어떤 작업의 성공, 실패 여부와 상관없이 항상 실행하고 싶은 콜백함수를 등록하는 메서드 입니다.
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json()) // 1번) promise 객체에 then 메서드를 연속적으로 붙이는 것을, 프로미스 체이닝 이라고 한다.
.then((data) => {
const users = data;
return users[0]; // 2번) then 메서드는 새로운 promise 객체를 리턴한다.
})
.then((user) => { // 3번) 즉, user 라는 새로운 promise 객체를 받는 것이다.
console.log(user);
const { address } = user;
return address;
})
.then((address) => {
console.log(address);
const { geo } = address;
return geo;
})
.then((geo) => {
console.log(geo);
const { lat } = geo;
return lat;
})
.then((lat) => {
console.log(lat);
});
fetch('https://learn.codeit.kr/api/interviews/summer')
.then((response) => response.json())
.then((interviewResult) => {
const { interviewees } = interviewResult;
const newMembers = interviewees.filter((interviewee) => interviewee.result === 'pass');
return newMembers;
})
.then((newMembers) => fetch('https://learn.codeit.kr/api/members', {
method: 'POST',
body: JSON.stringify(newMembers)
}))
.then((response) => {
if (response.status === 200) {
return response;
} else {
throw new Error('New members not added');
}
})
.then((response) => response.json())
.then((members) => {
console.log(`총 직원 수: ${members.length}`);
console.log(members);
});
fetch('https://jsonplaceholder.typicode.com/users')
/*
1번) promise 객체가 실패하면 실행하고 싶은 콜백함수는, then 메서드의 2번째 파라메터로 넣으면 된다.
실패하면, error에는 작업 실패 정보가 들어가게 된다.
주의사항은! 2번째 파라메터에서 에러처리를 하면, promise 상태가 pending -> fulfilled로 변화하기 때문에
(참고: promise 상태가 pending -> rejected -> fulfilled 되는 것 X)
*/
.then((response) => response.json(), (error) => { // 2번)
console.log(error);
return error; // 3번) 여기서 리턴한 값이된다.
})
.then((result) => { console.log(result); }); // 2번) 실패해도 다음 then으로 넘어가고, 대신 넘어온 promise 객체가
// 4번) 참고: 2번째 파라메터에서 에러처리를 하지않았는데, 1번째 파라메터에서 에러가 발생해야 -> rejected 상태!
// .then((response) => response.json())
// .then((result) => { console.log(result); });
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.catch((error) => { console.log(error); }) // 1번) catch는 promise 객체가 rejected 상태가 되면 실행할 콜백함수를 등록하는 메서드.
// .then(undefined, (error) => { console.log(error); }) // 2번) catch는 then 메서드의 1번째 인자에 undefined를 넣은 것과 같은 의미이다.
.then((result) => { console.log(result); }); // 3번) 실패해도 다음 then으로 넘어가고, 대신 넘어온 promise 객체가, catch에서 리턴한 값이된다.
// 4번) 그러나 보통, 어느 promise 객체가 rejected 상태가 되어도 잘 대응하기 위해서 -> catch 메서드는 맨 마지막에 작성한다. (실무에서 특히!)
.then((response) => response.json())
.then((result) => { console.log(result); }) // 5번) 대신, 어느 promise 객체에서 rejected 상태가 되었는지 판단하기 위해서, 이 안에서 추가 처리를 해준다. (예시에는 추가 처리를 해주지 않았음)
.catch((error) => { console.log(error); }); // 6번) 그러나, 어떤 promise 객체가 rejected 된 후에도 작업을 살릴 방법이 있다면
// .then((result) => { console.log(result); }); // 7번) catch 메서드를 사용한 후에도, then 메서드로 추가 작업을 해도 된다!
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((result) => { console.log(result); })
.catch((error) => { console.log(error); })
.finally(() => { console.log('END'); }); // 성공, 실패 여부와 상관없이 마지막에 작동한다 (finally 메서드는 보통, catch 메서드 뒤에 쓴다)
fetch('https://jsonplaceholder.typicode.com/users') // 1번) fulfilled 상태가 되면
.then((response) => response.json())
.then((result) => { console.log(result); console.log('A'); }) // 2번) 동작
.catch((error) => { console.log('B'); throw new Error('test'); })
.then((result) => { console.log(result); console.log('C'); }) // 3번) 동작
.then(undefined, (error) => { console.log('D'); })
.catch((error) => { console.log('E'); })
.then((result) => { console.log(result); console.log('F'); }) // 4번) 동작
.finally(() => { console.log('final'); }); // 5번) 동작
fetch('https://www.error.www') // 1번) rejected 상태가 되면
.then((response) => response.json())
.then((result) => { console.log(result); console.log('A'); })
.catch((error) => { console.log('B'); throw new Error('test'); }) // 2번) 동작
.then((result) => { console.log(result); console.log('C'); })
.then(undefined, (error) => { console.log('D'); }) // 3번) 동작 // 6번) rejected를 처리한 후에, 추가적으로 error를 던지지 않으면
.catch((error) => { console.log('E'); }) // 7번) 그 뒤에 연속적으로 연결되어있는 catch 구문은 실행되지 않는다.
.then((result) => { console.log(result); console.log('F'); }) // 4번) 동작
.finally(() => { console.log('final'); }); // 5번) 동작
new Promise 란?
- promise 객체를 직접 생성할 수 있다.
- resolve: 생성될 promise 객체를 fulfilled 상태로 만들 수 있는 함수가 연결됩니다.
- reject: 생성될 promise 객체를 rejected 상태로 만들 수 있는 함수가 연결됩니다.
then catch finally 메서드 기본 예시 1
const p = new Promise((resolve, reject) => {
setTimeout(() => { resolve('success'); }, 2000); // 1번) 2초후에 promise 객체가 fulfilled 상태가 되고, resolve(괄호)안에 값이
});
p.then((result) => { console.log(result); }); // 2번) 리턴값으로 넘어오게 된다. (result 값으로 넘어오는 것)
const p = new Promise((resolve, reject) => {
setTimeout(() => { reject('fail'); }, 2000); // 1번) 2초후에 promise 객체가 rejected 상태가 되고, reject(괄호)안에 값이
});
p.catch((error) => { console.log(error); }); // 2번) 리턴값으로 넘어오게 된다. (error 값으로 넘어오는 것)
function starbucks(coffename) {
const p = new Promise((resolve, reject) => {
if (coffename === "아메리카노") {
resolve("아메리카노 한잔입니다.");
} else {
reject("해당메뉴는 없습니다.");
}
});
return p;
};
starbucks("아메리카노") // 아메리카노 -> then + finally // 카페라떼 -> catch + finally
.then((resolve) => console.log(resolve)) // 아메리카노 한잔입니다. 감사합니다.
.catch((reject) => console.log(reject)) // 해당메뉴는 없습니다. 감사합니다.
.finally(() => console.log("감사합니다."));
// fulfilled 상태의 Promise 객체
const successPromise = Promise.resolve('success');
successPromise.then((result) => { console.log(result); }, (error) => { console.log(error); });
// rejected 상태의 Promise 객체
const failPromise = Promise.reject(new Error('fail'));
failPromise.then((result) => { console.log(result); }, (error) => { console.log(error); });
all 메서드 란?
- all 메서드도 then 메서드처럼 새로운 Promise 객체를 리턴합니다.
- 아규먼트로 들어온 배열 안에 있는 모든 Promise 객체가 pending 상태에서 fulfilled 상태가 될 때까지 기다립니다.
모든 promise 객체가 fulfilled 상태일 경우
- 모든 Promise 객체들이 fulfilled 상태가 되면, all 메서드가 리턴했던 Promise 객체는 fulfilled 상태가 됩니다.
- 그리고 각 Promise 객체의 작업 성공 결과들로 이루어진 배열을 all의 작업 성공 결과로 갖게 됩니다.
객체들 중 하나라도 rejected 상태가 되는 경우
- all 메서드는 하나의 Promise 객체라도 rejected 상태가 되면, 전체 작업이 실패한 것으로 간주합니다.
- 그리고 all의 promise 객체는 rejected 상태가 되고, 동일한 작업 실패 정보를 갖게됩니다.
코드 예시
// 1번 직원 정보
const p1 = fetch('https://learn.codeit.kr/api/members/1').then((res) => res.json());
// 2번 직원 정보
const p2 = fetch('https://learn.codeit.kr/api/members/2').then((res) => res.json());
// 3번 직원 정보
const p3 = fetch('https://learn.codeit.kr/api/members/3').then((res) => res.json());
Promise
.all([p1, p2, p3])
.then((results) => {
console.log(results); // [1번 직원 정보, 2번 직원 정보, 3번 직원 정보]
})
.catch((error) => {
console.log(error);
});
race 메서드 란?
- race 메서드도 all 메서드와 마찬가지로 여러 Promise 객체들이 있는 배열을 아규먼트로 받습니다.
- race 메서드가 리턴한 Promise 객체는 아규먼트로 들어온 배열의 여러 Promise 객체들 중에서, 가장 먼저 fulfilled 상태 또는 rejected 상태가 된 Promise 객체와 동일한 상태와 결과를 갖습니다.
- 말그대로 race 메서드는 여러 Promise 객체들을 레이스(race, 경쟁)시켜서 가장 빨리 상태가 결정된 Promise 객체를 선택하는 메서드입니다.
코드 예시
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Success'), 5000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('fail')), 2000); // 1번) 가장먼저 rejected 상태가 되기 때문에
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('fail2')), 4000);
});
Promise
.race([p1, p2, p3])
.then((result) => {
console.log('성공');
console.log(result);
})
.catch((value) => { // 2번) catch가 실행됩니다.
console.log('실패');
console.log(value);
});
allSettled 메서드 란?
- 배열 내의 모든 Promise 객체가 fulfilled 또는 rejected 상태가 되기까지 기다리고, pending 상태의 Promise 객체가 하나도 없게 되면 A의 상태값은 fulfilled 상태가 되고 그 작업 성공 결과로 하나의 배열을 갖게 됩니다.
- 이 배열에는 각 promise 객체의 (1) 최종 상태는 status 프로퍼티, (2) 그 작업 성공 결과는 value 프로퍼티, (3) 그 작업 실패 정보는 reason 프로퍼티가 객체요소로 담겨있습니다.
코드 예시
[
{status: "fulfilled", value: 1},
{status: "fulfilled", value: 2},
{status: "fulfilled", value: 3},
{status: "rejected", reason: Error: an error}
]