Thief of Wealth

 

정의

Promise는 자바스크립트 비동기 처리에 사용되는 객체입니다. (실제 연산을 직접 처리해주는 것은 아니고, 해당 연산을 대리하여 결과나 실패를 처리하기 위한 처리기와 연결할 수 있도록 하는 객체)

그리고 비동기란, "특정 코드의 실행이 완료될 때까지 기다리지 않고, 다음 코드를 먼저 수행하는 특성"을 말합니다.

탄생 배경

예전 작은 규모의 웹에서는 비동기 요청이 많지 않아서, 각 비동기 요청간 의존성이 크지 않았지만, 웹이 발전하고 다양한 플랫폼으로 진출하면서, 단순히 callback만으로는 모든 상태를 통제하기 어렵게 되었다.

그래서 기존 비동기 처리 코드들을 동일한 (표준) api형태로 사용할 수 있게 해주는 ES6에서 추가된 기능이다.

Promise 없이 Callback으로만 비동기 처리할 때 발생하는 문제점.
  1. 비동기 응답 의존관계에 따른 callback 중첩 (콜백지옥)
postRequest('/upload', function(req, res){
  var file = req.files[0];
  saveFile(file, function(originFileName){
    saveData(req.body, function(){
      res.send("ok");
    });
  });
});

위 코드 처럼, 각 비동기 요청이 순차적으로 일어나는 것을 보장해야 하는 경우에 콜백지옥이 발생할 수 있다.

2. 비동기 vs 비동기 간 경쟁

var a = null;
var b = null;

A(function(res){
  a = res;
  if(a !== null && b !== null){
    console.log(a + b);
  }
});
B(function(res){
  b = res;
  if(a !== null && b !== null){
    console.log(a + b);
  }
});

A,B 둘다 비동기 동작인데, A가 먼저 수행될수도, B가 먼저 수행될수도 있는 가능성을 가진다.

위 코드를 해결하려면, a,b값이 먼저 셋팅되었는지에 대한 작업을 콜백내 코드에 삽입해야되므로 코드가 엄청 더러워지고 유지보수가 어렵게 된다.

3. 비동기 결과값을 여러 분기가 사용하는 경우

readyToBatch(function(res){
  jobA(res);
  jobB(res);
  jobC(res);
});

1과 유사하지만, 각 작업간에 의존관계는 없는 상황이다. 중첩문제도 있지만, callback이 여러 분기에 대한 제어권을 갖기 때문에 좋지않고, 코드 테스트도 어렵다.

4. 신뢰성 문제

// 외부 라이브러리라 가정
var payLibrary = {
  pay: function(){
    // 결제 진행
  },
  requestPay: function (product, cb){
    // 라이브러리 개발자가 실수로 결제를 5번 진행하는 로직을 구현함
    for(var i=0; i<5; i++){
      var res = this.pay();
      cb(res);
    }
  }
};

function buyProduct(product){
  payLibrary.requestPay(product, function (payInfo){
    console.log(payInfo);
  });
}

buyProduct(someItem);

위 코드 처럼, 외부라이브러리에서 실수로 결제가 5번 진행되는 코드를 작성한다고 쳐보자, 이런 경우를 방지하기 위해서 callback 실행 시, flag같은 것을 추가하여 방어코드를 작성해야겠지만, 한번 외부라이브러리에 대한 신뢰성이 깨졌으므로 에러가 날 수 있는 모든 상황에 대해 방어코드를 작성하고 코드가 더러워 질 수 있음을 암시한다.

 

Promise를 사용해서 위 문제들 해결하기

 

1. 콜백지옥 해결

Promise Chain을 이용하여 콜백 지옥, Promise Hell을 해결할 수 있다. (depth가 안깊어짐)

 

2. 비동기 간의 경쟁은 Promise.all로 해결

var a = Promise.resolve(10);
var b = Promise.resolve(20);
Promise.all([a, b]).then(function([resA, resB]){
  console.log(resA + resB); // 30
});

a,b 모두 promise로 된 정의된 비동기작업일 때, Promise.all 메소드에 인자로 넣어주면, 인자로 넣어준 모든 비동기가 실행완료된 시점에 callback을 실행할 수 있도록 한다.

3. Promise는 한번만 실행된다.

이말은 프로미스로 생성된 비동기 인스턴스의 실행해 아무리 then을 날려도 이미 결정된 promise는 같은 결과값을 보여준다는 뜻.

즉, 비동기 결과값을 여러 분기에서 사용하더라도 항상 같은 값임을 보장할 수 있다.

function readyToBatch(){
  return Promise.resolve("result");
}

function jobA(ready){
  ready.then(function(batch){
    // ...
  });
}

var ready = readyToBatch();
jobA(ready);
jobB(ready);
jobC(ready);

4. 위 특성으로 신뢰성문제도 해결할 수 있다.

var p = new Promise(function(resolve, reject){
  for(var i=0; i<10; i++){
    resolve(i);
  }
});
p.then(console.log); // 0
p.then(console.log); // 0
p.then(console.log); // 0

같은 결과값이면 아무리 많이 실행되어도 다른 상태로 변화하지 않기 때문이다.

결론

Promise는 완전히 새로운 개념이 아니고, 비동기 처리에서 발생하던 안좋은 패턴들을 개선할 수 있도록 도와주는 새로운 비동기 표준이었던 것이다.

(https://yuddomack.tistory.com/entry/자바스크립트-콜백의-문제점과-프로미스-쓰는-이유)

실제 연산을 직접 처리해주는게 아니라, 해당 연산을 대리하여 결과나 실패를 처리하기 위한 처리기를 연결할 수 있도록 하는 객체인 것이다.

profile on loading

Loading...