Programming language/JavaScript

JavaScript Promise의 all( )과 race( )

iKay 2019. 3. 23. 14:45
반응형




JavaScript는 싱글쓰레드 이벤트 루프 방식으로 동작하기 때문에 동시성을 높이기 위해 최대한 비동기적으로 처리하는 것이 좋다. 시간이 오래 걸리는 어떤 I/O 연산에 의해 코드가 블록되면 그 이후 연산은 수행될 수 없기 때문이다. 웹 브라우저에서 사진을 받기 위해 몇 초를 기다리는 동안 다른 것을 할 수 없는 것을 경험한 적이 있을 것이다. 코드가 블록 되었기 때문이고 이런 부분은 가능하다면 비동기적으로 처리해주는 것이 좋다.


비동기 방식이 보통 더 효율적이지만 동기 방식에 비해 처리가 결코 쉽지않다. 처리 결과가 언제 어떻게 오는지 예측하기 힘들기 때문이다. 이제까지 JavaScript 코드에서 비동기 처리를 위한 여러가지 시도가 있는데 근래 들어서 Promise가 흔히 사용되는 것 같다. 아니 필수가 되었다. 콜백으로 처리하는 코드를 찾아보기 힘들 정도로 말이다.


JavaScript가 싱글쓰레드 이벤트 루프로 동작하지만 여러 I/O를 병렬적으로 처리하는 것도 충분히 가능하다. 여러 I/O를 동시에 병렬적으로 처리하기 위해  Promise의 all과 race를 사용할 수 있다. 오늘은 I/O 발생시 Promise all과 race를 이용해 병렬처리하는 법을 정리하고자 한다. 



Promis.all( )


Promise.all( )은 각 다른 (I/O든 아니든) 연산을 병렬로 처리하고, 모두 완료된 결과를 한 번에 리턴해준다. 아래 도식대로 두 데이터를 fetch 하는경우를 보자. 지난 글과 처리했던 방식을 비교해보면 좋을 것 같다. https://kay0426.tistory.com/29?category=791385



 

requestBoysHeights는 Promise 객체로 처리했고, requestGirlsHeights는 Promise 객체를 리턴하는 함수로 처리했다. ( 이 둘을 굳잉 왜 다르게 처리 했었을까...)


function timestamp () {
  return new Date().getTime();
};


function calculateAverage (values) {
  let arr, sum, avg;
  arr = values[0].concat(values[1]);
  sum = arr.reduce((acc, cur)=> acc + cur);
  avg = sum / (arr.length);
  return avg;
}


const requestBoysHeights = new Promise((resolve, reject) => {
  const heights = [175, 181, 165, 190, 166];
  setTimeout(resolve, 3*1000, heights);
});


function requestGirlsHeights () {
  return new Promise((resolve, reject) => {
    const heights = [156, 164, 171, 160, 178];
    setTimeout(resolve, 1*1000, heights);
  });
}


async function run() {
  const start = timestamp();
  Promise
      .all([requestBoysHeights, await requestGirlsHeights()])
      .then(values=> { // values: [ [ 175, 181, 165, 190, 166 ], [ 156, 164, 171, 160, 178 ] ]
        const end = timestamp();
        const avg = calculateAverage(values)
        console.log(avg)
        console.log(`It takes ${end-start}(ms)`);
      });
}


run();


결과, 다음과 같다. 남자의 키 데이터를 가져오는데 3초, 여자의 키 데이터를 가져오는데 1초 걸릴 경우, 동기적으로 처리하면 3+1초 걸리게 된다. 그러나 위와같이 병렬로 처리한다면 갖아 오래 걸리는 연산이 3초이므로 총 연산도 약 3초 걸리게 된다.  


170.6
It takes 3000(ms)


따라서 비동기적으로 혹은 병렬로 처리할 수 있는 연산은 블록하지 말고 비동기적으로 혹은 병렬로 처리하는 것이 훨씬 유리하다. 



Promise.race( )


Promise.race( ) 메쏘드는 각 다른 연산을 병렬로 처리하고 가장 빠른 결과만 처리한다. 어떤 연산 중 빠른 결과를 받아야 할 때 이 방식을 사용하면 될 것 같다. 


코드를 보자. 


function timestamp () {
return new Date().getTime();
};


const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 3*1000, 'promise1');
});


const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 1*1000, 'promise2');
});


function run() {
  const start = timestamp();
  Promise.race([promise1, promise2]).then(function(value) {
    const end = timestamp();
    console.log(value); // promise2
    console.log(`It just takes ${end-start}(ms)`);
  });
}


run();


결과는 다음과 같다. 


promise2
It just takes 1002(ms)



Promise.all( )과 Promise.race( )에 대해 살펴봤다. 이 둘 모두 병렬처리를 해야 할 때 적절히 상황에 맞게 유용하게 이용할 수 있을 것 같다. 





반응형