해당 게시글은 유튜브 '드림코딩' 채널의 영상을 참고해 작성되었습니다.
https://www.youtube.com/watch?v=JB_yU6Oe2eE&list=PLv2d7VI9OotTVOL4QmPfvJWPJvkmv6h-2&index=12&t=840s
Promise chaining
세 번째로 promise chaining 에 대해 예제를 통해 알아보겠습니다.
서버에서 숫자를 받아오는 새로운 프로미스를 만들어 볼 겁니다. 서버통신이라곤 하지만, 쉬운 이해를 돕기 위해 비동기 코드인 setTimeout 으로 대신하겠습니다. 프로미스는 resolve 라는 콜백함수를 받는 executor 라는 콜백 함수를 전달해 줘야 합니다. 그리고 서버 통신을 해야 하기 때문에 1 초 있다가 setTimeout 으로 숫자를 전달해주는 프로미스를 만들었습니다.
// 3. Promise chaining
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
})
위에서 말했듯이 이 프로미스는 1 초 있다가 숫자 1 을 전달할 겁니다. 그래서 원래대로라면 fetchNumber 라는 프로미스를 then 콜백함수의 매개변수를 통해 받을 건데, 해당 숫자를 두 배로 곱하고, 다시 그 숫자를 3 배로 곱하고, 또 다시 그 숫자를 이제는 다른 서버에 보내서 다른 숫자로 변환된 값을 받아오려고 합니다.
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));
그러기 위해 새로운 프로미스를 return 할 겁니다. 그래서 이 프로미스는 다른 서버랑 통신을 하게 되고, resolve 와 reject 를 받아서 다시 setTimeout 을 이용해 숫자에 1 을 뺀 값을 다시 resolve 를 통해 전달해 보도록 하겠습니다. 그 다음 해당 숫자를 콘솔에 출력해보도록 하겠습니다. 최종적으로 불러온 값인 num 을 쓰는 것이죠.
여기서 알아두어야 할 점은 then 의 새로운 역할인데요. then 은 값을 바로 전달할 수도 있지만 또 다른 비동기 프로미스를 전달할 수도 있습니다.
에러 핸들링
마지막으로 프로미스를 chaining 했을 때 어떻게 에러를 핸들링 할 수 있는지에 대해 알아보겠습니다. 예제를 보며 설명해보겠습니다.
// 4. Error Handling
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('chicken'), 1000);
});
const getEgg = hen =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${hen} => egg`), 1000);
});
const cook = egg =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => sunnyside`), 1000);
});
총 3 가지의 프로미스를 return 하는 함수가 있습니다.
- 처음 나온 getHen 은 암탉을 받아 오는 데요. 이 암탉을 받아오는 프로미스는 1 초 있다가 닭을 return 합니다.
- 그리고 getEGG 는 chicken 을 받아서 1초 뒤에 chicken 을 egg 를 얻어오는 프로미스를 return 합니다.
- 마지막으로 cook 은 egg 를 받아서 1초 뒤에 sunnyside 를 얻어오는 프로미스를 return 합니다.
이 세 함수를 chaining 해 서버에서 chicken 을 받아오고, egg 를 받아오고, sunnyside 까지 받을 수 있게 해보겠습니다.
// 4. Error Handling
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('chicken'), 1000);
});
const getEgg = hen =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${hen} => egg`), 1000);
});
const cook = egg =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => sunnyside`), 1000);
});
getHen()
.then(hen => getEgg(hen))
.then(egg => cook(egg))
.then(meal => console.log(meal));
먼저, 닭을 받아오고 나서 닭을 받는 데 성공하면 전달받은 닭을 이용해 getEgg 함수를 호출해줄 겁니다. 그리고 또 getEgg 함수가 정상적으로 수행이 되면 받아온 egg 를 이용해 cook 함수를 호출해줄 겁니다. 그리고 cook 함수가 정상적으로 수행되면 문자열이 출력될 겁니다. 이를 meal 이라는 매개변수로 받아 콘솔에 출력했습니다.
추가로 여기서 코드를 조금 깔끔하게 다듬을 수 있습니다. 콜백함수를 전달할 때 받아오는 value 를 다른 함수로 하나 호출하는 경우에는 이를 생략이 가능합니다.
// 변경 전
getHen()
.then(hen => getEgg(hen))
.then(egg => cook(egg))
.then(meal => console.log(meal));
// 변경 후
getHen()
.then(getEgg)
.then(egg => cook(egg))
.then(meal => console.log(meal));
그러면 자동적으로 받아오는 value 를 바로 getEgg 라는 함수에 전달해서 암묵적으로 전달해서 호출해줍니다. 그래서 위와 같이 한 가지만 받아서 그대로 전달하는 경우에는 아래와 같이 생략이 가능합니다.
// 변경 전
getHen()
.then(hen => getEgg(hen))
.then(egg => cook(egg))
.then(meal => console.log(meal));
// 변경 후
getHen()
.then(getEgg)
.then(cook)
.then(console.log);
만약 세 함수 중 네트워크에 문제가 생겨 실패가 되면 어떻게 될까요? 실패 될 때는 new Error 객체를 이용한다고 했습니다. 그래서 달걀을 받아오다가 에러가 발생했습니다.
여기서 어떤 에러 핸들링도 하지 않았기 때문에 "똑같이 에러를 핸들링 하지 않았네" 라고 찐 에러가 발생하는 것을 볼 수가 있습니다. 제일 마지막에 catch 를 통해 다시 한번 console.log 해주면,
달걀을 받아오는 부분에서 에러가 발생했지만 에러가 제일 밑에 있는 catch 로 전달되어 console.log 를 실행하는 것을 볼 수 있습니다.
여기서 만약 달걀을 받아올 때 무슨 문제가 생긴다면 준비한 다른 재료(chicken, sunnyside) 로 대체하고 싶다, 에러를 잘 핸들링하고 싶다면 그럴 땐 문제가 생긴 계란을 받아오는 과정에서 다른 재료를 전달해 주면 됩니다. 예를 들어 달걀 대신 빵을 전달해보겠습니다. 이 말은 즉 전달되어진 에러를 우리가 잘 처리해서 세 함수 중 문제가 생겨도 전체적인 promise chain 의 문제가 발생하지 않도록 해주는 것입니다. 아래와 같이요.
// 4. Error Handling
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('chicken'), 1000);
});
const getEgg = hen =>
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`error! ${hen} => egg`)), 1000);
});
const cook = egg =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => sunnyside`), 1000);
});
getHen()
.then(getEgg)
.catch(error => {
return 'bread';
})
.then(cook)
.then(console.log)
.catch(console.log);
비록 계란을 받아오는 것에 실패했지만 빵을 대신 전달해 주었기 때문에 프로미스 체인이 실패하지 않고 마지막 .then(console.log) 를 실행해 완성된 것을 확인할 수 있습니다. 그래서 에러를 처리하고 싶을 땐 바로 그 다음에 catch 를 작성하면서 문제를 해결할 수 있습니다. 만약 처리를 하지 않았을 경우, 아까처럼 Error: error! chicken => egg 가 출력되면서 프로미스가 완성되지 않는 것입니다.
'드림코딩 > 자바스크립트 기초 강의 (ES5+)' 카테고리의 다른 글
async / await (0) | 2023.03.24 |
---|