JavaScript

[Javascript] Promise

prefer2 2021. 10. 27. 18:27

 

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

 

콜백 헬

출처: https://adrianalonso.es/desarrollo-web/apis/trabajando-con-promises-pagination-promise-chain/

비동기처리를 위해서 콜백 함수를 사용하다보면 여러개의 콜백 함수가 중첩되는 상황을 경험한 경우가 많을 것이다. 특히 한 콜백 함수의 처리 결과를 가지고 다른 비동기 함수를 처리하고자(비동기 함수간에 처리 순서가 정해져 있을 때) 할 때 함수의 중첩이 일어나게 된다. 함수의 호출이 중첩(nesting)이 되어 복잡도가 높아지는 현상을 콜백 헬(Callback Hell)이라 한다. 전통적인 콜백 패턴은 콜백 헬로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한번에 처리하는 데도 한계가 있다.

ES6에서는 비동기 처리를 위한 또 다른 패턴으로 프로미스(Promise)를 도입했다. 프로미스는 전통적인 콜백 패턴이 가진 단점을 보완하며 비동기 처리 시점을 명확하게 표현할 수 있다는 장점이 있다.

 

프로미스의 3가지 상태(state)

프로미스의 상태에는 3가지가 있다. 여기서 말하는 상태란 프로미스의 처리 과정을 의미한다.

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

대기(pending) 중인 프로미스는 값과 함께 이행(fullfilled)될 수도, 어떤 이유(오류)로 인해 거부(rejected) 될 수 있다. 비동기 처리가 성공하면 콜백 함수의 인자로 전달받은 resolve 함수를 호출한다. 이때 프로미스는 ‘fulfilled’ 상태가 된다. 비동기 처리가 실패하면 reject 함수를 호출한다. 이때 프로미스는 ‘rejected’ 상태가 된다.

 

프로미스 생성

// Promise 객체의 생성
const promise = new Promise((resolve, reject) => {
  // 비동기 작업을 수행한다.

  if (/* 비동기 작업 수행 성공 */) {
    resolve('result');
  }
  else { /* 비동기 작업 수행 실패 */
    reject('failure reason');
  }
});

프로미스는 Promise 생성자 함수를 통해 인스턴스화한다. Promise 생성자 함수는 비동기 작업을 수행할 콜백 함수를 인자로 전달받는데 이 콜백 함수는 resolve와 reject 함수를 인자로 전달받는다.

💡 executor는 resolve나 reject 중 하나를 반드시 호출해야 한다. executor에 의해 처리가 끝난 일은 결과 혹은 에러만 가질 수 있다.

 

간단한 예제 (랜덤 숫자 뽑기)

const lotteryPromise = new Promise(function(resolve, reject) {
  //executor function
  console.log('랜덤 숫자가 0.5 이상이면 fulfilled 미만이면 rejected')

  setTimeout(function(){
    if(Math.random() >= 0.5) {
      resolve('You Win' // then에서 사용);
    }
    else{
      reject(new Error('You lost your money') // catch에서 사용);
    }
  }, 2000);
})

lotteryPromise.then(res=>console.log(res)).catch(err=>console.log(err))

 

then, catch

Promise로 구현된 비동기 함수는 Promise 객체를 반환한다. Promise로 구현된 비동기 함수를 호출하는 측(promise consumer)에서는 Promise 객체의 후속 처리 메소드(then, catch)를 통해 비동기 처리 결과 또는 에러 메시지를 전달받아 처리한다. Promise 객체 상태에 따라 후속 처리 메소드를 체이닝 방식으로 호출한다. 

👉 then: 두 개의 콜백 함수를 인자로 전달 받는다. 첫 번째 콜백 함수는 성공(fulfilled, resolve 함수가 호출된 상태) 시 호출되고 두 번째 함수는 실패(rejected, reject 함수가 호출된 상태) 시 호출된다. then 메소드는 Promise를 반환한다.
👉 catch: 예외(비동기 처리에서 발생한 에러와 then 메소드에서 발생한 에러)가 발생하면 호출된다. catch 메소드는 Promise를 반환한다.

 

const wrongUrl = 'https://naver.com/wrong_url';

// 부적절한 URL이 지정되었기 때문에 에러가 발생한다.
promiseAjax(wrongUrl)
  .then(res => console.log(res), err => console.error(err)); // Error: 404

비동기 처리 시에 발생한 에러는 then 메서드의 두 번째 콜백 함수로 처리할 수 있다. 하지만 이를 각 then 메서드에서 반복하다 보면 코드가 복잡해지고 가독성이 떨어지게 된다. 

catch 메서드를 모든 then 메서드를 호출한 이후에 호출하면 비동기 처리에서 발생한 에러(reject 함수가 호출된 상태)뿐만 아니라 then 메서드 내부에서 발생한 에러까지 모두 캐치할 수 있다. 이는 then 메서드에서 에러 처리를 하는 것보다 가독성이 좋다.

 

프로미스 체이닝(promise chaining)

function getData() {
  return new Promise({
    // ...
  });
}

// then() 으로 여러 개의 프로미스를 연결한 형식
getData()
  .then(function(data) {
    // ...
  })
  .then(function() {
    // ...
  })
  .then(function() {
    // ...
  });

프로미스는 후속 처리 메소드를 체이닝(chainning)하여 여러 개의 프로미스를 연결하여 사용할 수 있다. then 메소드가 Promise 객체를 반환하도록 하면(then 메소드는 기본적으로 Promise를 반환함) 여러 개의 프로미스를 연결하여 사용할 수 있다. 이로써 콜백 헬을 해결한다. 

 

간단한 예제

const timeCounter = new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})

timeCounter.then(function(result) {
  console.log(result); // 1
  return result + 10;
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

 

API 사용 예제

const getPosition = function(){
  return new Promise(function(resolve, reject){
    // navigator.geolocation.getCurrentPosition(
    //   position => resolve(position), 
    //   err =>reject(err)
    // )
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });
};

const whereAmI = function() {
    getPosition().then(pos=>{
      const {latitude: lat, longitude: lng} = pos.coords;
      return fetch(`https://geocode.xyz/${lat},${lng}?geoit=json&auth=XXX`)
    })
    .then(res=>{
      if(!res.ok) throw new Error(`Problem with geocoding ${res.status}`)
      console.log(res);
      res.json()
    })
    .then(data=>{
      console.log(data);
      console.log(`You ar in ${data.city}, ${data.country}`)
    })
    .catch(err=>console.log(err.message))
  }

  whereAmI();

 

참고

 

Promise | PoiemaWeb

Promise는 비동기 처리가 성공(fulfilled)하였는지 또는 실패(rejected)하였는지 등의 상태(state) 정보를 갖는다. Promise는 Promise 생성자를 통해 인스턴스화한다. Promise 생성자는 비동기 작업을 수행할 콜

poiemaweb.com

 

자바스크립트 Promise 쉽게 이해하기

(중급) 자바스크립트 입문자를 위한 Promise 설명. 쉽게 알아보는 자바스크립트 Promise 개념, 사용법, 예제 코드. 예제로 알아보는 then(), catch() 활용법

joshua1988.github.io

 

반응형

'JavaScript' 카테고리의 다른 글

[javaScript] export와 export default의 차이점  (0) 2021.11.30
[Javascript] Async, Await  (0) 2021.11.01
[Javascript] Object.create  (0) 2021.10.25
[Javascript] 상속(Inheritance)  (0) 2021.10.19
[Javascript] Class  (0) 2021.10.18