본문 바로가기

Language/JavaScript

<자바스크립트> 프라미스

 

1. 프라미스

* 소개

 - 제작 코드(producing code) : 원격에서 스크립트를 불러오는 것 같이 시간이 걸리는 코드

 - 소비 코드(consuming code) : 제작 코드의 결과를 사용하는 코드

 - 프라미스(promise) : 제작 코드와 소비코드를 연결해 주는 자바스크립트 객체

 

 - 프라미스 문법은 다음과 같다.

 

let promise = new Promise(function(resolve, reject) {
  // executor
});

 

 - 실행자(executor)의 인수 resolve와 reject는 자바스크립트가 자체적으로 제공하는 콜백이다.

 

 - 인수로 넘겨준 resolve와 reject중 하나를 반드시 호출해야 한다.

 

 - resolve(value) : 성공적으로 일이 끝난 경우 결과를 value와 호출

 - reject(error) : 에러 발생 시 에러 객체를 나타내는 error와 함께 호출

 

 - new Promise생성자가 반환하는 프라미스 객체는 state와 result라는 내부 프로퍼티를 갖는다.

 

 - state : 기본적으로 pending(보류)이며 resolve가 호출되면 fulfilled를, reject가 호출되면 rejected가 된다.

 - result : 기본적으로 undefined이며 resolve(value)가 호출되면 value를, reject(error)가 호출되면 error가 된다.

* 사용

 - 이제 promise생성자와 executor함수의 예시를 살펴보자. executor함수에는 즉시실행되는 함수를 넣을 수도 있지만 여러 요청을 보내는 상황을 가정하여 시간이 걸리는 setTimeout을 이용해보자.

 

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("완료!"), 1000);
});

 

 - 프라미스가 만들어지면 executor 함수는 자동으로 실행된다.

 

 - executor가 처리되고 1초 후에 resolve("완료!")가 후출되어 결과가 만들어진다.

 

 - 이 때의 프라미스 객체의 상태는 state가 pending에서 fulfilled로 바뀌고, result가 undefined에서 "완료!"로 바뀔 것이다.

 

 - 위처럼 일이 성공적으로 처리되면 "fulfilled promise(약속이 이행된 프라미스)"라 부른다.

 

 - 다음은 오류를 발생시키는 예시이다.

 

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("오류!")), 1000);
});

 

 - 이 때의 프라미스 객체의 상태는 state가 pending에서 rejected로 바뀌고, result가 undefined에서 "오류!"로 바뀔 것이다.

 

 - 참고로 이미 처리된 프라미스에 resolve와 reject를 호출할 경우 모두 무시된다.

 

2. 메서드

* then

 - 위에서 프라미스가 제작코드와 소비코드를 연결시켜 준다고 했었다. 여기서 소비코드는 then이나 아래에 나올 메서드들에 등록하여 사용한다.

 

 - then의 문법은 다음과 같다.

 

promise.then(
  function(result) {
    // ... 
  },
  function(error) {
    // ... 
  }
);

 

 - then의 첫 번째 인수는 프라미스가 제대로 이행되었을 때 실행되는 함수이다.

 

 - then의 두 번째 인수는 프라미스가 에러를 발생시켰을 때 실행되는 함수이다.

 

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("완료!"), 1000);
});

promise.then(
  result => alert(result),
  error => alert(error)
);

 

 - 위를 실행하면 프라미스가 제대로 이행되었다고 판단하고 result에 "완료!"를 받아서 보여준다.

 

 - 다음은 프라미스에서 에러가 났을 경우이다.

 

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("오류!")), 1000);
});

promise.then(
  result => alert(result), 
  error => alert(error) 
);

// Error: 오류!

 

 - 위의 경우 프라미스에서 에러가 났으며, error에 "Error: 오류!"가 담기게 된다.

 

 - 만약 성공 케이스만 다루고 싶다면 두 번째 인자를 제외시킬 수도 있다.

 

* catch 

 - 위에서 성공 케이스만 다루고 싶을경우에 대해서 간단히 얘기했었다. 만약 실패케이스만 다루고 싶다면 어떻게 해야할까.

 

 - 첫 번째 인자로 null을 넣어서 해도 좋지만 간단하게 catch를 쓰면 에러케이스만 다룰 수 있다.

 

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("오류!")), 1000);
});

promise.catch(alert); 

// Error: 오류!

 

 - 위는 .then(null, alert)와 완전히 동일하다.

 

* finally

 - finally는 try catch에서 나왔던 것과 거의 동일하다. 즉 프라미스가 성공이든 에러든 항상 실행된다.

 

 - 문법은 다음과 같다.

 

new Promise((resolve, reject) => {
  // ...
})
  .finally(() => /* ... */ )
  .then(result => /* ... */ )

 

 - finally 핸들러엔 인수가 없다. 즉 이 안에서는 프라미스가 성공인지 에러인지 알 수 없다. 물론 몰라도 되는 곳이다.

 

 - 여기서 놀라운 점은 result나 error가 finally를 거쳐서 자동으로 아래로 전달된다는점이다.

 

 - finally뒤에 catch로 에러를 다루더라도 정상적으로 작동한다.

 

* 프라미스 vs 콜백

 - 우리는 이제 프라미스가 콜백보다 나은 것을 알고 있다. 이를 정리해보자.

 

 - 콜백은 시간이 걸리는 작업을 호출할 때, 함께 호출할 callback함수를 미리 준비해야한다. (두 번째 인자에 넣음) 즉 호출 이전에 호출 결과로 무엇을 할 지 알고 있어야한다. 그러나 프라미스를 사용하면 .then에 코드를 작성하므로 흐름도 자연스럽고 가독성도 좋다.

 

 - 콜백은 하나만 가능하지만 프라미스는 원하는 만큼 then을 호출할 수 있다.

 

 

 


 

 

참고

 

 

모던 자바스크립트 튜토리얼

 

모던 JavaScript 튜토리얼

 

ko.javascript.info