관리 메뉴

Jerry

promise란? 본문

Front/JavaScript

promise란?

juicyjerry 2021. 5. 3. 00:11
반응형

이번 시간은 promise(프라미스 / 프로미스)에 대해서 알아보는 시간을 가지겠다.

JAVASCRIPT.INFO를 참고하여 정리를 해보았다!

 

promise

promise 객체는 다음과 같은 문법으로 만들 수 있다.

let promise = new Promise(function(resolve, reject) {
  // executor (제작 코드, '가수')
});

 

new Promise에 전달되는 함수는 executor(실행자, 실행 함수)라고 부른다. executor는 new Promise가 만들어질 때 자동으로 실행되며 인수로 resolve와 reject라는 자바스크립트 자체적으로 제공하는 콜백을 즉각적으로 반드시 호출해야 한다. 

 

resolve(value)는 에러 없이 성공적으로 일이 끝날 때, value와 함께 호출되며, reject(error)는 에러 발생 시 에러 객체를 나타낸다.

 

new Promise 생성자가 반환하는 promise 객체가 있는데, state, result라는 내부 프로퍼티를 갖는다. 

state에는 pending(보류 / 대기) / fullfied(resolve 호출시)호출 시) / rejected(reject 호출 시)가 있다. result는 undefined에서 resolve가 호출될 때 value로, reject가 호출되면 error로 변한다.

 

출처: JAVASCRIPT.INFO

 

 

 

출처: mdn

 

 

 

참고로, 처리가 끝난 프라미스에 resolve와 reject를 호출하면 무시된다.

 

let promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("…")); // 무시됨
  setTimeout(() => resolve("…")); // 무시됨
});

 

 

 

참고로, executor는 대개 비동기적인 동작을 수행하고, 약간의 시간이 흐른 뒤 resolve와 reject를 호출하는데, 꼭 이럴 필요까진 없으며 즉시 호출할 수 있다. 어떤 일을 시작하려고 했지만, 이미 일이 끝나 저장까지 된 경우에 사용할 수 있다고 한다.

 

let promise = new Promise(function(resolve, reject) {
  // 일을 끝마치는 데 시간이 들지 않음
  resolve(123); // 결과(123)를 즉시 resolve에 전달함
});

 

 

 

참고로, state result는 내부 프로퍼티이므로 개발자가 직접 접근할 수 없지만, .then /. catch /. finally 메서드를 사용하면 접근이 가능하다.

 

예를 들어, 프라미스로 비동기 처리 후에 관련 결과에 따라서 값을 처리하거나 에러 핸들링을 해야 하는데 혹은 하게 되는데... 이럴 때 사용할 수 있는 메서드가 then / catch / finally가 있는 것이다.

 

프라미스 핸들러 .then/catch/finally는 항상 비동기적으로 실행됩니다.

 

then 

then 메서드의 첫 번째 인수로 프라미스가 이행됐을 때 실행되는 함수로 실행 결과를 받는다. 두 번째 인수는 프라미스가 거부됐을 때 실행되는 함수로 에러를 받는다. 

 

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

// resolve 함수는 .then의 첫 번째 함수(인수)를 실행합니다.
promise.then(
  result => alert(result), // 1초 후 "done!"을 출력
  error => alert(error) // 실행되지 않음
);

 

 

catch

에러가 발생한 경우만 다루고 싶을 경우 사용하는 메서드다. 

프라미스에서 발생한 모든 에러를 다루며, reject()가 호출되거나 에러가 던져지면 .catch에서 이를 처리한다.

물론,. then(null, errorHandlingFunction)같이 null을 첫 번째 인수로 전달하면 되긴 하지만 이보다 catch 매소드가 주로 선호되는 것 같다.

 

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("에러 발생!")), 1000);
});

// .catch(f)는 promise.then(null, f)과 동일하게 작동합니다
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!" 출력
new Promise(function(resolve, reject) {
  setTimeout(() => {
    throw new Error("에러 발생!");
  }, 1000);
}).catch(alert);

 

 

 

finally

프라미스가 이행이 되거나 거부가 되면, 무조건 실행된다는 점에서 finally는. then(f, f)와 유사하다. 쓸모 없어진 로딩 인디케이터를 멈추는 경우 같이, 결과가 어쨌든 마무리해야 할 경우 유용하다. 

 

new Promise((resolve, reject) => {
  /* 시간이 걸리는 어떤 일을 수행하고, 그 후 resolve·reject를 호출함 */
})
  // 성공·실패 여부와 상관없이 프라미스가 처리되면 실행됨
  .finally(() => 로딩 인디케이터 중지)
  .then(result => result와 err 보여줌 => error 보여줌)

 

 

하지만, 차이점이 존재한다. 

finally 핸들러에는 인수가 없다. 단지, 보편적 동작만 수행하기 때문이어서 그렇다. 그래서 프라미스 상태(이행 / 거부)가 어떻게 되었는지 알 수도 없다.

 

new Promise((resolve, reject) => {
  throw new Error("에러 발생!");
})
  .finally(() => alert("프라미스가 준비되었습니다."))
  .catch(err => alert(err)); // <-- .catch에서 에러 객체를 다룰 수 있음

 

 

 

프라미스 체이닝

이번엔 프라미스 체이닝(promise chaining)을 이용한 비동기 처리에 대해 알아보자. 

프라미스 체이닝은 result가 .then 핸들러의 체인(사슬)을 통해 전달된다는 점에서 착안한 아이디어입니다.

 

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});

 

 

 

.then 또는 catch/finally 핸들러(어떤 경우도 상관없음)가 프라미스를 반환하면, 나머지 체인은 프라미스가 처리될 때까지 대기합니다. 처리가 완료되면 프라미스의 result(값 또는 에러)가 다음 체인으로 전달됩니다.

 

이를 그림으로 나타내면 아래와 같습니다.

 

 

출처: JAVASCRIPT.INFO

 

프라미스 체이닝이 가능한 이유는 promise.then을 호출하면 프라미스가 반환되기 때문이고 프라미스가 반환되기 때문에 .then을 호출할 수 있다.

 

초보자는 프라미스 하나에 .then을 여러 개 추가한 후, 이를 체이닝이라고 착각하는데, 이는 체이닝이 아닙니다.

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000);
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

 

 

출처: JAVASCRIPT.INFO

 

 

 

 

 

프라미스 API

Promise 클래스에는 5가지 정적 메서드가 존재한다. 

이 중에서 Promise.all을 실무에서 가장 많이 사용한다고 한다.

 

 

Promise.all(promises)

- 모든 프라미스가 이행될 때까지 기다렸다가 그 결괏값을 담은 배열을 반환하는데, 주어진 프라미스 중에 하나라도 실패하면 Promise.all는 거부되며, 나머지 프라미스도 무시된다.

 

Promise.allSettled(promises)

- 모든 프라미스가 처리될 때까지 기다렸다가 그 결과를 담은 배열을 반환한다.

- status는 fullfilled / rejected

- value / 실패한 reason

 

Promise.race(promises)

- 가장 먼저 처리된 프라미스의 결과 또는 에러를 담은 프라미스 반환함

 

Promise.resolve(value)

- 주어진 값을 사용해 이행 상태 프라미스 만듦

 

Promise.reject(error)

- 주어진 에러를 사용해 거부 상태 프라미스 만듦

 

 

 

 

출처: 

ko.javascript.info/promise-basics

ko.javascript.info/promise-chaining

ko.javascript.info/promise-error-handling

ko.javascript.info/promise-api

 

 

 

 

반응형