[Javascript] Promise
안녕하세요.
오늘은 Promise에 대해서 알아보도록 하겠습니다.
🔸 Promise란?
Promise란 현재에는 당장 값을 얻을 수 없지만, 미래에는 얻을 수 있는 데이터에 접근하기 위한 방법을 제공합니다.
현재 값을 얻을 수 없다는 것은 데이터를 얻는데 지연 시간이 존재한다는 것입니다. (대표적으로 network 통신으로 데이터를 얻는 경우가 있습니다.) 지연 시간이 발생하는 작업을 동기로 처리하면, 그 시간 동안 사용자는 브라우저를 사용할 수 없게 됩니다. 따라서 지연 시간이 발생하는 것들은 대부분 비동기로 처리하고 있습니다.
Promise는 비동기 처리에 사용되는 객체로, 콜백 지옥 문제를 해결하기 위해 ES2015에 공식적으로 추가되었습니다.
Promise가 등장하기 전에는 비동기 처리를 할 때 콜백을 중첩하여 사용하였는데, 이 코드는 읽기 어렵고, 에러가 발생해도 쉽게 찾지 못한다는 단점이 있었습니다.
Promise가 등장한 이후에는 비동기 처리 코드도 동기 처리 코드처럼 작성하여, 읽기 쉬운 코드를 생산할 수 있게 되었습니다.
- 비동기 처리란?
특정 코드의 연산이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것.
- 콜백 지옥이란?
비동기적 처리를 위해 콜백 함수를 반복해서 사용하는 것. 가독성이 떨어짐.
🔸 Promise 생성
Promise가 등장한 이유에 대해서 알아보았으니, 이제 promise를 생성하는 방법에 대해 알아보겠습니다.
Promise는 new Promise 메서드를 통해 생성할 수 있습니다. 이 생성자는 함수를 인자로 받으며, 이 함수 인자는 resolve, reject라는 파라미터를 가집니다.
//1. producer : 데이터 생성
const getData = new Promise((resolve, reject) =>{
let userData = '홍길동 20';
resolve(userData);
});
//2. consumers : then, catch, finally 데이터 소비
getData
.then(data => {console.log(data)})
.catch(err => {console.log(err)})
.finally(() => {console.log('finished')});
resolve 함수의 인자로는 미래 시점에 얻게 될 데이터를 넘겨주고, reject 함수의 인자로는 미래 시점에 발생할 예외를 넘겨줍니다. resolve의 인자는 then에서 받을 수 있고, reject의 인자는 catch에서 받을 수 있습니다. 위 코드에 이 개념을 대입해보겠습니다.
producer 부분에서 new Promise를 생성하였고, userData (홍길동 20)라는 것을 resolve에 넘겨주었습니다.
그럼 consumers 부분의 then으로 userData가 전달되게 되고, console에 이 데이터를 출력합니다.
근데 가장 아래에 then, catch 외에 finally라는 것이 더 있습니다. 이것은 then이 수행되느냐, catch 가 수행되느냐에 관계없이 가장 마지막에 한번 실행되는 코드입니다.
🔸 Promise의 상태
Promise는 대기, 이행, 실패 중 하나의 상태를 가집니다.
대기(Pending)
연산이 수행 중.
new Promise 메서드를 호출하면 대기 상태가 됩니다.
이행(Fulfilled)
연산이 성공적으로 완료됨.
콜백 함수의 인자 resolve를 실행하면 이행 상태가 됩니다.
이행 상태가 되면 then을 이용하여 처리 결과 값을 받을 수 있습니다.
실패(Rejected)
연산이 실패함.
콜백 함수의 인자 reject를 실행하면 실패 상태가 됩니다.
실패 상태가 되면 catch를 이용하여 실패한 이유를 받을 수 있습니다.
🔸 Promise 활용
Promise를 사용할 때 주의해야 할 점이 하나 있습니다.
new Promise를 선언하여 promise를 생성하면, 바로 실행되어 불필요한 네트워크 통신이 발생할 수 있습니다.
그래서 대부분은 함수 안에서 new Promise를 리턴하는 방법으로 promise를 사용합니다.
function devide(numA, numB){
return new Promise((resolve, reject) => {
if(numB === 0) reject(new Error("Cannot devide by 0"));
else resolve(numA/numB);
});
}
devide(6,3)
.then((result) => console.log("성공 :", result))
.catch((error) => console.log("실패 :", error))
.finally(() => console.log("Finished"));
// 성공 : 2
// Finished
devide(6,0)
.then((result) => console.log("성공 :", result))
.catch((error) => console.log("실패 :", error))
.finally(() => console.log("Finished"));
// 실패 : Error: Cannot devide by 0
// Finished
Chaining
이전 단계 비동기 작업의 결과 값을 다음 비동기 작업에서 이용할 때, Promise Chaining을 활용하여 코드를 더 보기 좋게 작성할 수 있습니다.
const f1 = () => {
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve("1번 주문 완료");
},1000);
});
};
const f2 = (message) => {
console.log(message);
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve("2번 주문 완료");
},3000);
});
};
const f3 = (message) => {
console.log(message);
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve("3번 주문 완료");
},2000);
})
};
console.log("시작");
f1()
.then(f2)
.then(f3)
.then(console.log)
.catch(console.log)
.finally(()=>{
console.log("끝");
})
then() 함수가 새로운 Promise를 반환하기 때문에 Chaining이 가능하며, 다음 Promise에 값을 전달하고 싶으면 반환 값을 설정해야 합니다.
위에서 1번, 2번, 3번을 순차적으로 주문하여 총 6초가 걸렸는데, 이것을 더 빠르게 하는 방법이 없을까요?
만약 세 사람이 각각 주문을 한다면, 가장 오래 걸리는 2번 주문의 소요시간인 3초 만에 주문이 가능하겠죠? 이때 쓸 수 있는 것이 Promise.all 함수입니다.
Promise.all
all 안에 배열로 함수를 전달해줍니다.
Promise.all([f1(), f2(), f3()])
.then(console.log);
//['1번 주문 완료', '2번 주문 완료', '3번 주문 완료']
all은 모두 실행이 완료되면, 값을 사용할 수 있습니다. 하지만 하나라도 reject가 되면 값을 사용할 수 없기 때문에 주의해야 합니다. 이 all 함수는 하나의 데이터라도 누락되면 페이지를 보여주면 안 될 때 사용할 수 있습니다.
all 함수와 비슷한데 다른 동작을 하는 race라는 함수에 대해서도 알아봅시다.
Promise.race
race도 all과 사용 방법은 동일합니다.
Promise.race([f1(), f2(), f3()])
.then(console.log);
//'1번 주문 완료'
race는 하나라도 끝낸다면 그 값을 리턴합니다. 용량을 큰 이미지들을 로딩할 때, 그중에 하나라도 완료되면 그 이미지를 보여주면 될 때 이 방법을 사용하면 됩니다.
오늘은 Promise에 대해서 알아봤는데요, all, race 함수는 잘 사용하면 유용할 것이라는 느낌이 드네요!
궁금한 점이나 틀린 내용이 있다면 댓글 부탁드립니다.
감사합니다.
출처 : https://joshua1988.github.io/web-development/javascript/promise-for-beginners/
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://www.youtube.com/watch?v=JB_yU6Oe2eE