TIL 22 | Promise

Saemsol Yoo·2020년 12월 25일
0

javascript

목록 보기
13/25
post-thumbnail

https://youtu.be/JB_yU6Oe2eE
드림코딩 by 엘리 님의 유튜브 강의를 보며 정리한 내용입니다.


1. Promise 개념

promise 는 자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트이다.
정해진 장시간의 기능을 수행하고 나서

  • 정상적으로 기능이 수행되어졌다면 → 성공의 메세지와 함께 처리된 결과값을 전달
  • 만약 기능을 수행하다가 예상치 못한 문제가 발생했다면 → 에러를 전달해준다.

비동기적인 것을 수행할 때 콜백 함수 대신에 유용하게 쓸 수 있는 오브젝트이다. 🙂


1-1. state

state : pending → fulfilled or rejected

  • promise의 state는 promise가 만들어 져서 우리가 지정한 operation이 수행중일 때는 pending 상태가 되고,
  • 이 operation을 성공적으로 다 끝내게 되면 바로 fulfilled 상태가 된다.
  • 아니면, 파일을 찾을 수가 없거나 네트워크에 문제가 생긴다면 rejected 상태가 된다.

1-2. producer vs consumer

Producer vs Consumer

  • 그리고 promise에는 우리가 원하는 기능을 수행해서 해당하는 데이터를 만들어내는 Producer
    (= 즉 project object)
  • 이 제공된 데이터를 필요로 하거나, 소비하는 Consumer로 나누어 진다.



2. Producer

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-1. producer 만들기

어떤 일을 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( ) 콜백함수를 통해 전달해준다.


👏🏻 이렇게 promise 라는 Producer를 만들었으니, 이제 이것을 이용하는 Consumer를 만들어보자.



3. Consumers

consumersthen 이나 catch 그리고 최신에 추가된 finally 를 이용해서 값을 받아올 수 있다.

3-1. then

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로 전달 되어져서 들어오는 것이다.

3-2. catch

만약 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 했던 것처럼!)

3-3. finally

최근에 추가된 .finally 는 성공하든 실패하든 상관없이 무조건 마지막에 호출되어지는 것이다. 그래서 성공과 실패 상관없이 어떤 기능을 마지막으로 수행하고 싶다면 finally 를 사용하면 된다.

promise(
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.log(error);
  })
  .finally(() => {  //아무런 인자를 받지 않고 콘솔로그를 출력,,
    console.log("finally");
    // 실패했을 때도 error 콜백함수가 처리되고 나서 "finally"가 호출되고,
    // 성공했을 때도 그에 따른 콜백함수가 수행된 후 "finally"가 호출된다.
  });
);



4. Promise chaining

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 여러개를 동시에 묶어서, 또는 다른 비동기적인 것들도 묶어서도 처리할 수 있다.



5. Error handling

아래는 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);  //🐓 => 🥚 => 🍳

5-1. error handling: 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)
  .then(cook)
  .then(console.log); //catch를 처리해주지 않으면 여기서 에러가 발생한다.

이제 에러핸들릉을 해주기 위해 마지막에 catch 를 추가해준다.

getHen()  //
  .then(getEgg)
  .then(cook)
  .then(console.log)
  .catch(console.log);  //Error: error! 🐓 => 🥚

→ 달걀을 받아오는 부분(getEgg)에서 에러가 발생했지만, 이 에러가 밑으로 전달되면서 catch가 잡혀져 콘솔창으로 에러메세지가 출력된다.


5-2. 에러를 대체하기

getHen()
  .then((hen) => getEgg(hen))
  .catch((error) => {  //여기서 계란을 받아올 때 문제가 생긴다면,
    return "🍞";       //우리가 다른 것을 전달해 줄 거다.
  })
  .then((egg) => cook(egg))
  .then((meal) => console.log(meal))
  .catch(console.log);  // 🍞 => 🍳

→ 비록 계란을 받아오는(getEgg) 것은 실패했지만 빵을 대신 전달해 줬기 때문에 이 promise chain이 실패하지 않고 결국 마지막 요리까지 완성시켰다.

💡 그곳에서 발생하는 에러를 처리하고 싶을 때는 → 바로 그 다음에(바로 이어서) catch 를 작성하면서 바로바로 문제를 해결하도록 한다.



6. 콜백지옥 코드개선

하지만 이것도 더 개선 가능 😇

profile
Becoming a front-end developer 🌱

0개의 댓글