프로미스(Promise)란?
비동기처리에서 흔하게 사용되는 콜백함수는 여러번 중첩될 경우 콜백지옥의 문제점이 생긴다. 프로미스는 콜백을 대신해 비동기 처리에 사용하는 객체다.
예제
새로운 프로미스가 생성될때 (new Promise) 프로미스 안에 있는 콜백함수가 바로 실행된다.
const promise = new Promise((resolve, reject) => {
// 비동기적으로 실행하고 싶은 코드를 작성
// doing some heavy work (network, read files)
console.log('doing something...')
})
프로미스의 2가지 결과값 (성공과 실패)
- 성공: 정해진 시간에 수행했을 경우 → 성공의 메시지와 함께 처리된 결과값을 전달
- 실패: 예상치 못한 문제가 발생할 경우 → 에러를 전달
프로미스의 3가지 상태 (state)
1. pending (대기)
비동기 처리 로직이 아직 완료되지 않은 상태
new Promise(); // 메서드를 호출하면 대기상태
메서드를 호출할 때 콜백함수를 선언할 수 있고, 콜백함수의 인자는 resolve, reject다
new Promise(function(resolve, reject) {
// ...
});
2. fulfilled (완료)
비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
// 콜백함수의 인자 resolve를 실행하면 이행상태
const promise = new Promise((resolve, reject) => {
// 비동기적으로 실행할 코드
resolve(); // 비동기 코드가 실행한 후 실행됨
});
resolve 함수가 실행되면(이행 상태가 되면) then 메서드로 처리 결과 값을 받는다
promise.then(value => {
console.log(value); // resolve 값 전달
})
3. rejected (실패)
// 콜백함수의 인자 reject를 실행하면 실패상태
const promise = new Promise((resolve, reject) => {
// 비동기적으로 실행할 코드
reject(); // 비동기 코드가 실행한 후 실행됨
});
reject 함수가 실행되면(실패 상태가 되면) catch 메서드로 처리 결과 값을 받는다
promise.catch(error => {
console.log(error); // reject 값 전달
})
producer(정보 제공자)와 consumer(정보 소비자)의 차이점
Producer: 원하는 기능을 수행하고 해당하는 데이터를 만든다
- resolve: Promise가 정상적으로 수행 시 resolve를 반환한다.
- reject: Promise에 오류가 발생하는 경우에는 reject를 반환한다. 이때, 에러가 발생하는 이유를 적어두는 것이 좋다.
const promise = new Promise((resolve, reject) => { // Promise State: Pending 대기
// 비동기적으로 실행하고 싶은 코드를 작성
console.log('doing something...'); // doing some heavy work (network, read files)
setTimeout(() => {
// resolve 또는 reject를 사용해야 한다
resolve('ellie') // Promise State: fulfilled 완료
reject(new Error('no network')) // Promise State: rejected 실패
}, 2000);
});
Consumer: then, catch, finally로 값을 처리한다
- then: then은 Promise가 정상적으로 수행했을 경우 resolve 콜백함수를 통해 전달받은 값을 파라미터로 전달받는다.
- catch: catch는 Promise에서 에러 발생시 reject 콜백함수를 통해 전달받은 값을 파라미터로 전달받는다.
- finally: finally는 Promise의 결과와 상관없이 마지막에 무조건 실행된다.
promise // 오브젝트에서 여러 함수를 연속적으로 호출하는 체이닝 기법을 사용
.then(value => {
console.log(value); // resolve 값 전달
})
.catch(error => {
console.log(error); // reject 값 전달
})
.finally(() => {
console.log('finally'); // 결과에 상관없이 무조건 전달
});
프로미스 실전 예제
function getData() {
return new Promise(function(resolve, reject) {
$.get('url 주소/products/1', function(response) {
if (response) {
resolve(response);
}
reject(new Error("Request is failed"));
});
});
}
// 위 $.get() 호출 결과에 따라 'response' 또는 'Error' 출력
getData().then(function(data) {
console.log(data); // response 값 출력
}).catch(function(err) {
console.error(err); // Error 출력
});
프로미스 체이닝
프로미스 체이닝이란 여러개의 프로미스를 연결해서 사용하는 방식을 말한다.
function getData() {
return new Promise({
// ...
});
}
// then() 으로 여러 개의 프로미스를 연결한 형식
getData()
.then(function(data) {
// ...
})
.then(function() {
// ...
})
.then(function() {
// ...
});
프로미스 체이닝 예제
then은 값을 전달할 수도 있고 promise를 전달 할 수도 있다.
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
});
fetchNumber
.then(num => num * 2)
.then(num => num * 3)
.then(num => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(num - 1), 1000);
});
})
.then(num => console.log(num));
프로미스 에러 핸들링
프로미스로 비동기통신을할때 에러가 발생할 수 있다. 이때 발생한 에러는 catch() 함수로 처리해준다.
const getHen = () =>
new Promise((resolve, reject)=>{
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = hen =>
new Promise((resolve, reject)=>{
setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000);
});
const cook = egg =>
new Promise((resolve, reject)=>{
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen()
.then(getEgg) // 이곳에서 발생하는 에러를 처리하고 싶을때
.catch(error => { // catch문을 사용해서 에러를 핸들링해준다
return `🥖`; // getEgg에서 에러가 발생할때 대신 사용
})
.then(cook)
.then(console.log)
/*
콜백함수를 전달할때 받아오는 value를 이렇게 다른 함수를 바로 호출하는 경우에는 value를 생략할 수 있다
getHen()
.then(hen => getEgg(hen))
.then(egg => cook(egg))
.then(meal => console.log(meal));
*/
프로미스의 2가지 API
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple() {
await delay(2000);
return '🍎';
// throw 'error';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
1. 프로미스 체인 사용
문제점: 콜백지옥처럼 중첩되는 체이닝이 반복된다 & 함수 getApple()과 getBanana()는 함수에서 리턴값을 받아오는 시간이 각각 1초씩 걸린다 → async 키워드를 이용하자
function pickFruits() {
return getApple().then(apple => {
return getBanana().then(banana => `${apple} + ${banana}`);
});
}
pickFruits().then(console.log);
2. async & await 사용
문제점: 함수 getApple()과 getBanana()는 함수에서 리턴값을 받아오는 시간이 각각 1초씩 걸린다 (비효율적) → 동시에 수행하는 Promise의 API를 이용
async function pickFruits() {
const apple = await getApple();
const banana = await getBanana();
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
3. all: 동시에 리턴값을 받아오는 프로미스 API
function pickAllFruits() {
return Promise.all([getApple(), getBanana()])
.then(fruits => fruits.join(' + '));
}
pickAllFruits().then(console.log);
4. race: 먼저 리턴되는 하나의 값을 받아오는 프로미스 API
function pickOnlyOne() {
return Promise.race([getApple(), getBanana()])
}
pickOnlyOne().then(console.log);
Reference
'JavaScript' 카테고리의 다른 글
| [자바스크립트] 호이스팅(Hoisting)이란? (0) | 2022.11.22 |
|---|---|
| [자바스크립트] async & await (0) | 2022.11.20 |
| [자바스크립트] 콜백함수와 콜백지옥 (0) | 2022.11.18 |
| [자바스크립트] 동기적 코드와 비동기적 코드 (0) | 2022.11.18 |
| [자바스크립트] 이벤트 관리 방법 (버블링, 캡쳐링, 이벤트 위임) (0) | 2022.11.18 |
댓글