본문 바로가기

JavaScript

비동기함수의 순서를 제어하는 방법 2. Promise

Callback Hell을 벗어나기 위한 방법 2가지 존재.

1. Promise
2. async / await

이번 시간에는 'Promise'에 대해 알아보자.

< Promise객체 >

  • 자바스크립트에서 제공하는 '비동기'를 간편하게 처리할 수 있도록 도와주는 object(즉, 객체).
  • Promise is a JavaScript object for 'asynchronous' operation.
  • 자바스크립트 안에 내장되어 있는 Object.
  • 비동기적인 것을 수행할 때 콜백함수 대신에 유용하게 쓸수있는 object.
  • 정해진, 장기간의 기능을 수행하고 나서, 정상적으로 기능이 수행되어졌다면 성공의 message와 함께 처리된 결과값을 전달해 준다.
  • 생성자는 매개변수로 "실행 함수(executor)"를 받습니다. --> ex: new Promise(() => {})
  • 이 "실행함수"는 매개 변수로 또다른 두 가지 함수를 받음. 첫 번째 함수(resolve)는 비동기 작업을 성공적으로 완료해 결과를 값으로 반환할 때 호출해야 하고, 두 번째 함수(reject)는 작업이 실패하여 오류의 원인을 반환할 때 호출. 두 번째 함수는 주로 오류 객체를 받습니다. --> ex: new Promise ((resolve, reject) => {resolve('network ok') or reject(new Error('no network'))})

** 보통 promise안에는 조금 heavy한 일들을 한다. 네트워크에서 데이터를 받아오거나, 파일에서 큰 데이터를 읽어오는 과정은 시간이 꽤 걸린다.
이걸 동기적으로 처리하게 되면 파일을 읽어오는 동안 다음 코드가 실행되지 않기 때문에 시간이 조금 걸리는 일들은 promise를 만들어서 비동기적으로 수행하는 것이 좋다. 그래서 network통신 또는 파일을 읽어온다던지 이런건 비동기적으로 처리하는 것이 좋다.

 

 

< Promise의 2가지 중요 포인트 >

1.State : 프로세스가 무거운 operation을 수행하고 있는지, 아니면 기능수행이 완료되어서 성공했는지, 실패했는지 등의 상태.

   1> pending

  • Promise가 만들어져서 operation이 실행중일 때.
  • resolve, reject 둘다 호출하지 않은 상태.

** promise를 만드는 순간, executor라는 콜백함수가 바로 실행된다.

const promise = new Promise((resolve, reject) => { console.log('doing something...') })

위 함수는 바로 console창에 'doing something'이 뜬다. 

  2> fulfilled : resolve 있고, user.then 없어도 된다.

  3> rejected : .catch없이 reject함수 실행된 것.

 

 

 

2.Producer(정보제공자 : Producer object) vs Consumer(정보소비자)

 1> Promise 만들기
      Promise 객체는 new 키워드 생성자를 사용해 만든다. --> ex: new Promise()

 const promise = new Promise((resolve, reject) => {
 console.log('doing something...');
 setTimeout(() => {
 resolve('ellie');
 },2000);
 })

2> Promise 사용하기
Consumers: then, catch, finally를 통해 값을 받아올 수 있다.

    1.  then

promise
.then(value => console.log(value)) //2초 후, console창에 'ellie'뜬다. 
//promise가 정상적으로 잘 수행된다면, then값을 받아온다.
//resolve라는 콜백함수를 통해서 전달한 값이 value의 parameter로 전달되어진다. 

  2. catch

 const promise = new Promise((resolve, reject) => {
 setTimeout(() => {
 reject(new Error('no network')); //reject는 보통 Error라는 object를 통해서 값 전달.
 //Error : 자바스크립트에서 제공하는 object 중 하나. 무언가 에러가 발생했다는 걸 나타내는 object.
 //Error object : 어떤 에러가 발생했는지 이유를 잘 명시해서 작성.
 },2000);
 })

 promise
.then(value => console.log(value))
 //이렇게만 적으면 콘솔창에 Uncaught(in promise) Error: no network 이렇게 에러가 뜬다.
 //Uncaught: then으로 성공적인 케이스만 다뤘기 때문에 잡히지 않은 에러가 발생 --> .catch로 에러 처리해야한다. 
 .catch(error => {console.log(error)}); //After 2 seconds, Error: no network 이렇게 뜬다. 

 

3. finally

성공, 실패 상관없이 무조건 마지막에 호출되어지는 것.

//Producer
const promise = new Promise((resolve, reject) => {
    console.log('doing something...'); //promise 객체 만들어지면 바로 실행된다.
    setTimeout(() => {
       resolve('ellie');
     },2000);
 })
 
 //Consumer
 promise.
.then(value => console.log(value)) //after 2 secs, 'ellie'뜬다. 
.catch(error => {console.log(error)})
.finally(() => console.log('finally')); //'ellie'와 같이 'finally'뜬다.

//콘솔창에는 'doing something'먼저 뜨고, 2초 후 'ellie', 'finally' 뜬다.

 

 

 

<예제 : callback함수 사용 시>

const printString = (string, callback) => {
setTimeout(
() => {
console.log(string)
callback() //인자로 callback을 추가로 받고 setTimeout함수에 callback함수 추가
},
Math.floor(Math.random() * 100) + 1)}

const printAll = () => {
 printString("A", () => { //console.log("A") 실행
   printString("B", () => { // console.log("B") 실행
    printString("C", () => {}) //console.log("C") 실행
    })
  })
}

printAll()

 

 

<예제 : promise 사용 시>

const printString = (string) => {
return new Promise((resolve, reject) => {
  setTimeout(
    () => {
    console.log(string)
    resolve()
   },
  Math.floor(Math.random() * 100) + 1)
 })

const printAll = () => {
 printString("A") //then으로 넘길때는 'Promise객체' or '값'이여야한다. 이 작업(console.log(string), resolve())이 끝나면 아래작업.해줘. 
 .then(() => {
   return printString("B") //'Promise 객체'가 넘겨지고 있다. 
  })
 .then(() => {
   return printString("C") //'Promise 객체'가 넘겨지고 있다.
  })
}

printAll()

Promise를 사용하면 훨씬 가독성이 좋고, 평평하다.
callback과 동일하게 실행된다.

 

 

3. Promise 연결하기(Promise chaining)

Promise Hell도 존재한다.

Promise Hell을 피하기 위해서 Promise chaining 잘해줘야한다.

 

 

[Promise Hell vs Promise Chaining example]

//Producer
function gotoCodestates(){
  return new Promise ((resolve, reject) => {
   setTimeout(() => {resolve('1. go to codestates')}, 100)
   })
}

function sitAndCode(){
  return new Promise ((resolve, reject) => {
   setTimeout(() => {resolve('2. sit and code')}, 100)
   })
}

function eatLunch(){
  return new Promise ((resolve, reject) => {
   setTimeout(() => {resolve('3. eat lunch')}, 100)
   })
}

function goToBed(){
  return new Promise ((resolve, reject) => {
   setTimeout(() => {resolve('4. goToBed')}, 100)
   })
}

//Consumer - Promise Hell
gotoCodestates()
.then(data => {

     console.log(data) //'1. go to codestates'
     sitAndCode() //then이 끝나지 않고 계속 이어진다. 
     .then(data => {
       console.log(data) //'2. sit and code'
       eatLunch()
       .then(data => {
           console.log(data) //'3. eat lunch'
           goToBed()
          .then(data => {
            console.log(data) //'4. goToBed'
            })
       })
  })


})

//Consumer - Promise Chaining
gotoCodestates()
.then(data => {
  console.log(data) //'1. go to codestates'
  return sitAndCode() //꼭 'return'을 해야한다!! 
  //return값으로 promise객체 전달
 })
.then(data => {
  console.log(data) //'2. sit and code'
  return eatLunch() // return값으로 promise객체 전달
 })
.then(data => {
  console.log(data) //'3. eat lunch'
  return goToBed() // return값으로 promise객체 전달
})
.then(data => {
  console.log(data) //'4. goToBed'
})
//Producer
const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000)
});

//Consumer
fetchNumber //promise객체 전달
.then(num => num * 2) //return값은 2. 값 전달
.then(num => num * 3) //return 값 : 6. 값 전달 여기까지 1초 걸림.
.then(num => {
   return new Promise((resolve, reject) => {
   setTimeout(() => resolve(num - 1),1000)
   });
 }) //promise객체.
.then(num => console.log(num)); //여기서 1초 걸림. 최종적으로 2초 후 '5'가 콘솔창에 뜬다.

즉, then에서는 '값'을 전달해도 되고, 'promise 객체'를 전달해도 된다.

 

4. Error Handling

//Producer
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);
  });
  
//Consumer
getHen()
  .then(hen => getEgg(hen)) //hen === '🐓', return값이 promise객체. 여기서 reject 에러.
  // `error!  🐓 => 🥚`. 이것이 바로 catch로 넘어간다.
  .then(egg => cook(egg)) // 
  .then(meal => console.log(meal))
  .catch(meal => console.log(meal));
  
  //console창에 'Error: error! 🐓 => 🥚' 이렇게 뜬다.

'JavaScript' 카테고리의 다른 글

part3  (0) 2021.09.21
fetch() API  (0) 2021.09.21
callback vs promise vs async & await  (0) 2021.09.20
타이머 API ( setTimeout, setInterval, clearInterval)  (0) 2021.09.19
비동기 함수의 순서를 제어하는 방법 3. async & await  (0) 2021.09.19