JavaScript - Promise

Noma·2021년 1월 30일
0
post-custom-banner

1) Promise란?


비동기 연산이 받아올 미래의 완료(또는 실패) 및 그 결과값을 나타냅니다.

- 특징

  1. 비동기를 (콜백함수 대신에) 간편하게 처리할 수 있도록 도와주는 오브젝트이다.

  2. Promise를 사용하면 마치 synchronous한 것처럼 값을 반환할 수 있다. 이는 미래의 어떤 시점에 최종 결과값이 아닌 Promise 자체를 반환한다.

  3. 정해진 (장시간의) 기능을 수행하고 나서 정상적으로 수행되면 성공과 함께 처리된 결과값을 전달해주고, 만약 기능 수행 중 문제가 발생했다면 에러를 전달해준다.

2) State

pending → fulfilled or rejected
: 프로세스가 heavy한 오퍼레이션을 수행하고 있는 중인지, 기능 수행이 완료되어 성공했는지, 실패했는지와 같은 상태를 나타낸다.

- 대기(pending)

: 이행하거나 거부되지 않은 초기 상태

- 이행(fulfilled)

: 연산이 성공적으로 완료된 상태

- 거부(rejected)

: (오류가 나서) 연산이 실패한 상태

3) Producer - 데이터 생성

: 프로미스 안의 코드를 수행한 후 전달하고자하는 데이터를 만든다. 즉, Promise Object를 말함

🌟 Doing some heavy work ( network, read files ) !
그 이유는, heavy한 것들을 synchronous하게 처리하게 되면 뒤에 있는 코드들이 실행되지 않기 때문에 시간이 좀 걸리는 일들은 promise를 만들어 비동기적으로 처리하는 것이 좋다.

const promise=new Promise((resolve,reject)=>{ 
    console.log('doing something...');
    setTimeout(()=>{
       // resolve('merry'); 
       reject(new Error('no network')); 
    },2000)
});

(resolve,reject)=>{do something...}
: executor라는 콜백함수를 전달해야함
Promise를 만드는 순간 전달된 excuter라는 콜백함수가 바로 실행된다.

- resolve

: 기능 정상 수행 후 마지막의 최종 데이터를 전달하는 콜백함수

- reject

: 중간에 문제 발생시 호출하게 될 콜백함수로, 보통 Error object를 통해서 값(new Error(~))을 전달함

4) Consumer - 데이터 소비

then, catch, finally 를 통해 값을 받아올 수 있음

promise
    .then(value => console.log(value))
    .catch(error => console.log(error))
    .finally(() => console.log('finally'));
// 'value=>console.log(value)'는 'console.log'로 줄여서 표현가능함

- then

: resolve에서 전달된 값을 인자로 받아옴 → 성공시 어떻게 처리할 건지 콜백함수 등록

  • Promise를 리턴하기 때문에 바로 .catch.then을 써서 chaining 할 수 있음
  • .then은 값을 바로 전달해줘도 되지만 다른 새로운 Promise를 만들어 전달해줄수도 있음
.then(num=>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>resolve(num-1),1000);
  })
})
  .then(num=>console.log(num));

- catch

: reject에서 전달된 값을 인자로 받아옴 → 에러 발생시 어떻게 처리할 건지를 다룸

  • catch(failureCallback)는 then(null, failureCallback)의 축약이다.
  • catch로 에러를 핸들링해주지 않으면 reject로 에러를 받아왔을때 Uncaught Error라고 뜨게 된다.

- finally

: 성공 여부에 상관없이 마지막에 호출되어지는 함수

6) Promise Chaining

보통 한두 개 이상의 비동기 작업이 순차적으로 실행되어야 하는 경우가 흔하다. 이전 단계의 비동기 작업이 성공하고 나서 그 결과값을 이용해 다음 비동기 작업을 실행하게 된다. 이럴때 사용하면 좋은 것이 promise chaining이다.

다만, promise도 과하게 nesting할 경우 callback hell과 비슷해지므로 주의해야한다.

const fetchNumber=new Promise((resolve, reject)=>{
    setTimeout(()=>resolve(1),1000);
});
console.log(fetchNumber);

Promise를 생성하면 바로 executor가 실행되고 성공 여부에 따라 상태와 결과값이 Promise로 반환된다.

[console]

Promise {<fulfilled>: 1}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: 1

만약 resolve나 reject가 등록되지 않으면 상태는 "pending"이며 결과값은 undefined이게 된다.

Promise Chaining에 대해선 아래 예제을 읽어보자!

const fetchNumber=new Promise((resolve, reject)=>{
    setTimeout(()=>resolve(1),1000);
});
fetchNumber
.then(num=>num*2) //num에는 resolve에서 전달받은 1이 들어가게 되고 성공적으로 연산이 완료되면 .then에 결과값을 전달하게 됨
.then(num=>num*3)//반복...
.then(num=>{
    return new Promise((resolve,reject)=>{
        setTimeout(() => resolve(num-1),1000);
    }); //❗then은 값을 바로 전달할 수도, promise를 전달할 수도 있음
})
.then(num=>console.log(num));
[console]

5

7) Error Handling

chain에서 작업이 실패하더라도 catch를 통해 처리가 가능하다.
아래는 비동기적으로 암탉과 계란, 계란후라이를 얻어와 console에 출력하는 예제이다.

const getHen=()=>
  new Promise((resolve,reject)=>{
      setTimeout(()=>resolve('🐔'),1000);
  });

const getEgg=hen=>
  new Promise((resolve,reject)=>{
      setTimeout(() =>resolve(`${hen}=> 🥚`),1000);
    //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)
[console]

🐔=>🥚=>🍳

에러가 생겼을 경우엔 어떻게 처리하는지 살펴보자!

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) 
.catch(error=>'🍞')  
.then(cook) 
.then(console.log); 
[console]
🍞=>🍳
  • 받아오다가 문제가 생겨도 전체적인 Promise Chain에 문제가 발생하지 않도록 빵꾸 처리가능 (예제에선 에러를 받아와 '🍞'으로 리턴해서 이어감)
  • 발생하는 에러를 처리하고 싶을때는 바로 다음 줄에 캐치를 작성해서 문제 해결 가능
  • 만약 바로 다음에 에러 핸들링을 안했을 경우, getEgg에서 실패했기 때문에 요리가 완성(🐔=>🥚=>🍳)되지 않는 것을 확인할 수 있음

❗ 참고자료
https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Using_promises
https://www.youtube.com/channel/UC_4u-bXaba7yrRz_6x6kb_w

profile
오히려 좋아
post-custom-banner

0개의 댓글