https://youtu.be/JB_yU6Oe2eE
드림코딩 by 엘리 님의 유튜브 강의를 보며 정리한 내용입니다.
promise 는 자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트이다.
정해진 장시간의 기능을 수행하고 나서
✨ 비동기적인 것을 수행할 때 콜백 함수 대신에 유용하게 쓸 수 있는 오브젝트이다. 🙂
state : pending → fulfilled or rejected
Producer vs Consumer
when new Promise is created, the executor runs automatically !
우리가 원하는 기능을 비동기적으로 실행하는 promise를 만들어 보자. promise는 class 이기 때문에 new
라는 키워드를 이용해서 object를 생성할 수 있다.
그리고 promise에는 executor 이라는 콜백함수를 전달해 주어야 하는데, 이 콜백함수는 또 두가지의 콜백함수(resolve, rejcet)를 받는다.
const promise = new Promise((resolve, reject) => {
// doing some heavy work (network, read files)
console.log('doing something...'); // doing something... 이 바로 출력된다.
});
→ 보통은 promise안에서 조금 무거운 일들을 한다. 왜냐하면 우리가 네트워크에서 데이터를 받아 오거나 파일에서 무언가 큰 데이터를 읽어오는 과정은 시간이 꽤 걸리는데, 그런 것들을 동기적(=asynchronous)으로 처리하게 되면 우리가 파일을 읽어오고 네트워크에서 데이터를 받아오는 동안 그다음 라인의 코드가 실행되지 않기 때문에 시간이 조금 걸리는 일들은 이렇게 promise로 만들어서 비동기적으로 처리하는 것이 좋다.
🤚🏻 promise를 만드는 순간! 우리가 전달한 executer라는 콜백함수가 바로 실행이 된다. 이 말은 즉, 우리가 promise안에 네트워크 통신을 하는 코드를 작성했다면, promise가 만들어지는 그 순간 바로 네트워크 통신을 수행하게 된다는 것이다.
그러므로 만약 네트워크 요청을 사용자가 요구했을 때만 해야 되는 경우라면, 위와 같은 방식으로 작성하게 될 때는 사용자가 요구하지도 않았는데 불필요한 네트워크 통신이 일어나게 되는 것이므로 promise가 만들어지는 순간 그 안에 전달한 executer이라는 콜백함수가 바로 실행이 된다는 점을 유의해야 한다.
어떤 일을 2초 정도 하다가 결국에는 일을 잘 마무리해서 resolve
라는 콜백함수를 호출하면서, 'eden'이라는 값을 전달해주는 promise를 만들어보자.
const promise = new Promise((resolve, reject) => {
//doing some heavy work
console.log("doing something...");
setTimeout(() => {
resolve("eden"); // 성공적으로 잘 마쳤어! .... resolve 콜백함수 호출
}, 2000);
});
기능을 잘 수행했다면 → resolve( ) 라는 콜백함수를 호출하며 성공적으로 받아온 데이터를 그 resolve( ) 콜백함수를 통해 전달해준다.
consumers는 then
이나 catch
그리고 최신에 추가된 finally
를 이용해서 값을 받아올 수 있다.
2-1
에서 만든 promise라는 변수를 이용해 값이 정상적으로 잘 수행이 된다면
→ .then 을 사용해서 value를 받아와가지고 우리가 원하는 기능을 수행하는 콜백함수에 전달해준다.
// 2-1에서 만든 promise
const promise = new Promise((resolve, reject) => {
//doing some heavy work
console.log("doing something...");
setTimeout(() => {
resolve("eden"); // 성공적으로 잘 마쳤어! .... resolve 콜백함수 호출
}, 2000);
});
promise.then((value) => { //value가 전달된다.
console.log(value); // ...2초 뒤 "eden" 출력된다.
//이렇게 value가 전달되면 이것을 콘솔로그에 출력해준다.
});
value : value라는 parameter는 promise가 정상적으로 잘 수행이 된 결과, 마지막으로 resolve 콜백함수로 전달한 "eden" 이라는 값이 들어오게 된 것이다.
즉, then 이라는 것은 promise가 정상적으로 잘 수행이 되어서 최종적으로 resolve라는 콜백함수를 통해 전달한 값이 value parameter로 전달 되어져서 들어오는 것이다.
만약 network를 하다가 무언가 실패했다면, resolve 대신에 이제는 reject를 호출해보자.
그리고 .catch 라는 함수를 이용해서 error가 발생했을 때 어떻게 처리할 건지 콜백함수를 등록한다.
✨ reject는 보통 error (new Error) 라는 object를 통해서 값을 전달한다.
//producer
const promise = new Promise((resolve, reject) => {
//doing some heavy work (network, readfiles)
console.log("doing something...");
setTimeout(() => {
//resolve("eden");
reject(new Error("no network")); //어떤 에러가 발생했는지 이유를 잘 써주기!
}, 2000);
});
//consumer
promise
.then((value) => { //성공한 거 받기
console.log(value);
})
.catch((error) => { //실패(error) 받기
console.log(error);
});
Error : Error 라는 class는 자바스크립트에서 제공하는 object 중 하나이다. 무언가 error가 발생했다는 것을 나타내주며 보통 error object에는 어떤 에러가 발생했는지 이유를 잘 명시해서 작성해야 한다.
💡 chaining .then
을 호출하게되면 그 then은 결국 다시 똑같은 promise를 return 하고, 그 return된 promise에 .catch
를 등록할 수 있다. 이런 것을 chaining이라고 한다. (array에서 .map().sort().join() ... 이렇게 chaining 했던 것처럼!)
최근에 추가된 .finally 는 성공하든 실패하든 상관없이 무조건 마지막에 호출되어지는 것이다. 그래서 성공과 실패 상관없이 어떤 기능을 마지막으로 수행하고 싶다면 finally 를 사용하면 된다.
promise(
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
})
.finally(() => { //아무런 인자를 받지 않고 콘솔로그를 출력,,
console.log("finally");
// 실패했을 때도 error 콜백함수가 처리되고 나서 "finally"가 호출되고,
// 성공했을 때도 그에 따른 콜백함수가 수행된 후 "finally"가 호출된다.
});
);
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)); //2초뒤 ... 5 출력
→ 이렇게 .then .then .then .then 여러개를 동시에 묶어서, 또는 다른 비동기적인 것들도 묶어서도 처리할 수 있다.
아래는 promise를 return하는 세가지의 함수들이다.
//암탉을 받아오는 promise는 1초 있다가 닭을 return한다.
const getHen = () =>
new Promise ((resolve, reject) => {
setTimeout(() => resolve("🐓"), 1000);
});
//get Egg는 닭을 받아서 그 닭으로부터 => 달걀을 얻어오는 promise를 return한다.
const getEgg = (hen) =>
new Promise ((resolve, reject) => {
setTimeout(() => resolve(`${hen} => 🥚`), 1000);
});
// cook은 달걀을 받아와서 달걀을 가지고 fried egg를 만드는 함수이다.
const cook = (egg) =>
new Promise ((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen()
.then((hen) => getEgg(hen))
.then((egg) => cook(egg))
.then((meal) => console.log(meal)); //🐓 => 🥚 => 🍳
🤚🏻 콜백함수를 전달할 때 이렇게 받아오는 value를 다른 함수로 바로 호출하는 경우, 생략해서 적어줄 수 있다. ✂️
getHen() // 프리티어 포멧에서 한 줄로 바꿔버리기 때문에 이렇게 주석 표시해주면 엔터 처리 가능하다!
.then(getEgg)
.then(cook)
.then(console.log); //🐓 => 🥚 => 🍳
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)
.then(cook)
.then(console.log); //catch를 처리해주지 않으면 여기서 에러가 발생한다.
이제 에러핸들릉을 해주기 위해 마지막에 catch 를 추가해준다.
getHen() //
.then(getEgg)
.then(cook)
.then(console.log)
.catch(console.log); //Error: error! 🐓 => 🥚
→ 달걀을 받아오는 부분(getEgg)에서 에러가 발생했지만, 이 에러가 밑으로 전달되면서 catch가 잡혀져 콘솔창으로 에러메세지가 출력된다.
getHen()
.then((hen) => getEgg(hen))
.catch((error) => { //여기서 계란을 받아올 때 문제가 생긴다면,
return "🍞"; //우리가 다른 것을 전달해 줄 거다.
})
.then((egg) => cook(egg))
.then((meal) => console.log(meal))
.catch(console.log); // 🍞 => 🍳
→ 비록 계란을 받아오는(getEgg) 것은 실패했지만 빵을 대신 전달해 줬기 때문에 이 promise chain이 실패하지 않고 결국 마지막 요리까지 완성시켰다.
💡 그곳에서 발생하는 에러를 처리하고 싶을 때는 → 바로 그 다음에(바로 이어서) catch
를 작성하면서 바로바로 문제를 해결하도록 한다.
하지만 이것도 더 개선 가능 😇