ES6+ 향상된 비동기 프로그래밍(2) - 프로미스 활용
ES6에서 Promise가 javascript 언어에 포함이 되었습니다.
- 이제 콜백 지옥에서 벗어나 보자.
이전글 [React] - ES6+ 향상된 비동기 프로그래밍(1) - 프로미스
1. 프로미스 활용하기
#1 Promise(프로미스) 병렬처리: Promise.all
then 메서드로 프로미스 chain을 만들어 순차적으로 실행하기.
requestData1()
.then(data => {
console.log(data);
return requestData2();
})
.then(data => {
console.log(data);
})
병렬로 실행 되는 코드
requestData1().then(data => console.log(data));
requestData2().then(data => console.log(data));
Promise.all 사용
Promise.all([requestData1(), requestData2()])
.then( ([data1, data2]) => {
console.log(data1, data2);
});
Promise.all 함수는 프로미스를 반환한다.
입력된 모든 프로미스가 settled(처리됨) 상태가 되어야 마찬가지로 settled(처리됨) 상태가 된다.
만약 하나라도 rejected(거부됨) 상태가 된다면 Promise.all 함수가 반환하는 프로미스도 rejected(거부됨) 상태가 된다.
#2 가장 빨리 처리된 프로미스 가져오기: Promise.race
말 그대로 여러 프로미스 중 가장 빨리 settled(처리됨) 상태가 된 프로미스를 반환하는 함수다.
Promise.race를 사용한 간단한 예제
Promise.race([
requestData(),
new Promise((_, reject) => setTimeout(reject, 3000)),
])
.then(data => console.log(data))
.catch(error => console.log(error));
requestData 함수가 3초 안에 데이터를 받으면 then
그렇지 않으면 catch 메서드가 호출된다.
#3 프로미스를 이용한 데이터 캐싱
데이터를 가져오는 작업에 실패하는 경우가 고려되지 않은 간단한 예제
let cachedPromise;
function getData() {
cachedPromise = cachedPromise || requestData(); // 1
return cachedPromise;
}
getData().then(v => console.log(v));
getData().then(v => console.log(v));
1번에서 처음 호출할 때만 requestData 함수가 호출된다.
2. 프로미스 사용 시 주의사항
#1 return 키워드 깜빡하지 않기
Promise.resolve(10)
.then(data => {
console.log(data);
Promise.resolve(20); // need return
})
.then(data => {
console.log(data) // undefined
});
then 메서드 내부 함수에서 return 키워드를 입력하는 것을 깜빡하기 쉽다.
#2 Promise는 불변 객체
프로미스가 수정된다고 생각하고 작성한 코드
function requestData() {
const p = Promise.resolve(10);
p.then(() => { // 기존 객체 수정 X
return 20; // 새로운 Promise 반환
});
return p;
}
requestData().then(v => {
console.log(v) // 10
});
이제 원하는 값 20이 나오게 하려면 아래아 같이 수정해야 한다.
function requestData() {
return Promise.resolve(10).then(v => {
return 20;
});
}
requestData().then(v => {
console.log(v) // 20
});
#3 Promise를 중첩해서 사용하지 않기
콜백 패턴 만큼 복잡해지므로 지양해야 하는 중첩 코드
requestData1().then(data1 => {
requestData2(data1).then(data2 => {
//...
});
});
아래와 같이 사용하는 것이 좋다.
requestData1()
.then(data1 => {
return requestData2(data1);
})
.then(data2 => {
//... // 1번
});
1번 에서 data1 변수를 참조해야 한다면?
Promise.all 함수를 사용해 중첩하지 않고 해결할 수 있다.
requestData1()
.then(data1 => {
return Promise.all([data1, requestData2(data1)]);
})
.then(([data1, data2]) => {
//...
});
#4 동기(sync) 코드 예외 처리
동기 코드에서 발행한 예외가 처리되지 않는 코드
function requestData() {
doSync(); // 동기 코드
return fetch()
.then(data => console.log(data))
.catch(error => console.log(error));
}
fetch 전에 doSync 함수가 반드시 호출되어야 하는 게 아니라면 아래와 같이 then 메서드 안쪽으로 넣어 주는 게 좋다.
function requestData() {
return fetch()
.then(data => {
doSync(); // 동기 코드
console.log(data);
})
.catch(error => console.log(error));
}
이제 catch에서는 doSync 함수에서 발생하는 예외 처리가 된다.