본문 바로가기

Back-End/NodeJS

ES2015 이후의 비동기 제어 흐름 패턴( Asynchronous Control Flow pattern ) -node.js

ES2015 이후의 비동기 제어 흐름 패턴


아직 자바스크립트를 사용해보지 않아, 콜백지옥이 얼마나 심각한지는 모른다. 뭐 무튼 불편하니까 계속 대안이 나오고 있지 않을까? 


이번 포스팅에서 유명한 대안책인 Promises, Generators / async, await ( ECAMA 2017 ) 에 대해 알아볼 것이다.


Promises

Promise는 비동기 작업의 결과를 제어할 수 있는 object를 반환해주는 함수이다. ( 말이 이해하기 힘들다.... 그냥 비동기 작업의 결과가 성공/실패일 때 제어할 수 있도록 제공해주는 함수이다.) 

promise.then([onFulfilled], [onRejected])


기존의 콜백 지옥

asyncOperation(arg, (err, result) => { 
  if(err) { 
    //handle error 
  } 
  //do stuff with result 
}); 


Promise를 사용한 구조 

asyncOperation(arg) 
  .then(result => { 
    //do stuff with result 
  }, err => { 
    //handle error 
  });


Promise가 필요한 이유?

프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용됩니다. 아래코드는 일반적으로 사용되는 것입니다.

$.get('url 주소/products/1', function (response) {
  // ...
});

데이터를 받아오기전에 화면에 표시하려고 하면 오류가 발생하거나 빈 화면이 뜹니다.이를 해결하기 위한 방법 중 하나가 Promise 입니다.

Promise의 상태

  • Pending ( 대기 ) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled ( 이행 ) : 비동기 처리가 완료되어 결과값을 반환해준 상태
  • Rejected ( 실패 ) : 비동기 처리에서 오류가 난 상태

이때까지 이해한 Promise를 바탕으로 하나의 예제를 작성해 봤습니다. 아래 코드는 서버 products/1로부터 data를 받아 제대로 된 값이 오면 resolve처리하고, 실패하면 reject 처리하는 것 입니다.



function getData() {
  return new Promise(function (resolve, reject) {
    $.get('url 주소/products/1', function (response) {
      if (response) {
        resolve(response);
      }
      reject(new Error("Request is failed"));
    });
  });
}

// Fulfilled 또는 Rejected의 결과 값 출력
getData().then(function (data) {
  console.log(data); // response 값 출력
}).catch(function (err) {
  console.error(err); // Error 출력 

});


또 다른 예제를 살펴보면, 사용자 정보를 받아와 파싱하고, 인증하고 등등 작업하는 것입니다. 그리고, 아래 코드처럼 프로미스를 연결하여 사용할 수도 있습니다. 


getData(userInfo)
  .then(parseValue)
  .then(auth)
  .then(diaplay);


var userInfo = {
  id: 'test@abc.com',
  pw: '****'
};

function parseValue() {
  return new Promise({
    // ...
  });
}
function auth() {
  return new Promise({
    // ...
  });
}
function display() {
  return new Promise({
    // ...
  });
}



Node.js 스타일 : promisify


module.exports.promisify = function(callbackBasedApi) { 
  return function promisified() { 
    const args = [].slice.call(arguments); 
    return new Promise((resolve, reject) => {        //[1] 
      args.push((err, result) => {                   //[2] 
        if(err) { 
          return reject(err);                        //[3] 
        } 
        if(arguments.length <= 2) {                  //[4] 
          resolve(result); 
        } else { 
          resolve([].slice.call(arguments, 1)); 
        } 
      }); 
      callbackBasedApi.apply(null, args);            //[5] 
    }); 
  } 
};