프라미스(Promise)

GODORI·2018년 10월 14일
7
post-thumbnail

🌱 Background Story

JavaScript는 단일 스레드 기반으로, 두 스크립트를 동시에 실행할 수 없으며 차례로 실행해야 한다.

JS에서 실행될 준비가 된 코드 조각은 Job Queue(작업 대기열)에 보관된다. 코드 실행이 끝나면, 이 대기열을 관리하는 Event Loop에서 다음에 대기중인 작업을 실행한다.

Javascript에서 비동기적으로 결과를 얻기 위한 방법으로 다음과 같은 패턴이 존재한다.

☝️ Event를 이용하는 방법

마우스 클릭이나 키보드 입력 등의 특정한 이벤트가 발생하면 Job Queue의 뒤쪽에 새로운 작업이 추가된다. 이벤트를 처리할 이벤트 핸들러 코드는 이벤트가 발생하기 전까지는 실행되지 않는다. 이것이 가장 기본적인 비동기 프로그래밍이라고 할 수 있다.

이벤트는 동일 객체에 여러 번 발생할 수 있는 간단한 작업에는 좋지만, 멀티 스레드 작업에 최선의 방법은 아니다. 가령, 여러 개의 비동기 호출을 함께 연결해야 한다면 먼저 각 이벤트에 대한 대상을 추적해야 한다. 또한 이벤트가 발생하기 전에 이벤트 핸들러를 추가해야 한다.

✌️Callback을 이용하는 방법

JavaScript에서 함수는 1급 객체이므로, 함수를 파라미터로 전달할 수 있다. 콜백 패턴은 호출할 함수가 파라미터로 전달된다는 점에서 이벤트와 차이가 있다. 이렇게 파라미터로 전달한 함수는 Job Queue의 끝에 추가되고, 콜백 함수를 호출한 함수의 작업이 끝나면 실행된다.

콜백은 이벤트보다 비교적 여러 개의 호출을 함께 연결하기 쉽다. 하지만 연속적인 비동기 처리를 위해 너무 많은 콜백을 중첩하여 사용했을 때 문제가 발생한다. 이는 콜백 지옥이라 불리는 것으로 콜백 속에 콜백, 그리고 콜백 속에 또 콜백이 이어지는 이해하기 어려운 코드를 만든다.

🤝 프라미스(Promise)

프라미스란?

프라미스는 콜백의 단점을 해결하려는 시도 속에서 만들어졌다. 하지만 프라미스가 콜백을 대체하는것은 아니며, 프라미스에서도 콜백을 사용한다. 다만, 예측 가능한 패턴으로 콜백을 사용하게 하여 콜백만 사용 시 찾기 힘든 버그를 해결한다

프라미스는 이벤트 리스너와 유사하나, 다음의 차이가 있다.

  • 프라미스는 딱 한 번만 성공 또는 실패한다.
  • 프라미스가 성공 또는 실패한 후에 각각의 콜백을 추가하면 이벤트가 더 일찍 발생해도 올바른 콜백이 호출된다.

이는 무언가 사용 가능해진 정확한 시간을 아는 것보다 결과에 따른 응답이 중요한 비동기 작업에 유용하다.

프라미스의 상태

프로미스는 상호 배타적인 세 가지 상태를 가진다.

  • 처리됨 fulfilled : 프로미스와 관련된 작업이 성공적으로 수행됨
  • 거부됨 rejected : 프로미스와 관련된 작업이 실패함
  • 보류됨 pending : 처리(fulfilled)되거나 거부(rejected)되지 않은 상태

프라미스의 결과는 성공(fulfilled)하거나 실패(rejected)하거나 단 두 가지 뿐이며, 이 때 결정됐다(settled)라고 한다. 확실한 성공 혹은 실패 두 가지 중 단 하나만 딱 한번 일어난다. 성공했다가 실패하는 건 없다.

프라미스와 함께 쓰이는 resolved라는 단어는 다른 프라미스의 상태에 맞춰 처리되거나 거부되어 상태가 고정(locked in)되었다는 의미이다.

콜백과 달리 프라미스는 객체이므로 어디든 전달할 수 있다. 따라서 비동기 처리를 다른 함수로 넘겨 처리하게 할 수도 있다.

프라미스 예제

프라미스를 만드는 기본적인 방법은 resolvereject 콜백이 있는 함수로 Promise 인스턴스를 생성하는 것이다.

    // seconds만큼의 카운트다운이 끝나면 프라미스 반환하는 함수
    function contdown (seconds){
      return new Promise(
    		funciton(resolve, reject){
    	    for(let i=seconds; i >=0; i--){
    	      setTimeout(function(){
    	        if(i>0)
    	          console.log(i+ '...');
    	        else
    	          resolve(console.log('GO!'));
    	      }, (seconds-1) * 1000);
    	    }
      });
    }

    // 사용법 1 : then으로 결과를 바로 처리하기
    countdown(5).then({
      function(){ 
    		console.log('success!'); 
    }, function(err){
    		console.log('error!');
    } })

    // 사용법 2 : 프라미스를 변수에 할당한 후 then과 catch를 이용해 처리하기
    const p = countdown(5);
    p.then(function(){
    	console.log('success');
    });
    p.catch(function(){
    	console.log('error');
    });
  • resolve 와 reject는 함수이지만, 첫 번째만 호출한 것만 의미가 있다. (여러번 호출해도 의미없다)

  • 사용법 1: 반환된 프라미스를 변수에 할당하지 않고 then 핸들러 호출 후 성공 혹은 에러 콜백을 받는다.

  • 사용법 2: 프라미스를 변수에 할당한다. 프라미스는 catch 핸들러도 지원하므로 둘로 나눠서 써도 된다.

참고

러닝 자바 스크립트 : ES6로 제대로 입문하는 모던 자바스크립트 웹 개발(이선 브라운, 한빛미디어)
https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md
https://developers.google.com/web/fundamentals/primers/promises?hl=ko

profile
잡식개발

0개의 댓글