개발자공부일기

Promise 본문

Javascript

Promise

JavaCPP 2025. 2. 4. 21:23

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

설명

Promise 는 프로미스가 생성된 시점에는 알려지지 않았을 수도 있는 값을 위한 대리자로, 비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 처리기를 연결할 수 있습니다. 프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하는 것이 아니고, 미래의 어떤 시점에 결과를 제공하겠다는 '프로미스(promise)'를 반환합니다.

 

Promise는 다음 중 하나의 상태를 가집니다.

  • 대기(pending): 이행하지도, 거부하지도 않은 초기 상태.
  • 이행(fulfilled): 연산이 성공적으로 완료됨.
  • 거부(rejected): 연산이 실패함.

대기 중인 프로미스는 값과 함께 이행할 수도, 어떤 이유(오류)로 인해 거부될 수도 있습니다. 이행이나 거부될 때, 프로미스의 then 메서드에 의해 대기열(큐)에 추가된 처리기들이 호출됩니다. 이미 이행했거나 거부된 프로미스에 처리기를 연결해도 호출되므로, 비동기 연산과 처리기 연결 사이에 경합 조건은 없습니다.

프로미스가 이행되거나 거부되었지만 보류 중이 아닌 경우, 프로미스가 확정된 것으로 간주합니다.

 

 

사용법

let myFirstPromise = new Promise((resolve, reject) => {
  // 우리가 수행한 비동기 작업이 성공한 경우 resolve(...)를 호출하고, 실패한 경우 reject(...)를 호출합니다.
  // 이 예제에서는 setTimeout()을 사용해 비동기 코드를 흉내냅니다.
  // 실제로는 여기서 XHR이나 HTML5 API를 사용할 것입니다.
  setTimeout(function () {
    resolve("성공!"); // 와! 문제 없음!
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage는 위에서 resolve(...) 호출에 제공한 값입니다.
  // 문자열이어야 하는 법은 없지만, 위에서 문자열을 줬으니 아마 문자열일 것입니다.
  console.log("와! " + successMessage);
});

//출력: 와! 성공!

 

프로미스 연결

Promise.prototype.then(), Promise.prototype.catch()  Promise.prototype.finally() 메서드는 정착된 프로미스와 추가 작업을 연결하는 데 사용됩니다. 이러한 메서드는 프로미스를 반환하므로 연쇄적으로 연결할 수 있습니다.

.then() 메서드는 최대 두 개의 인수를 받습니다. 첫 번째 인수는 프로미스의 이행된 경우에 대한 콜백 함수이고, 두 번째 인수는 거부된 경우에 대한 콜백 함수입니다. 각 .then()은 새로 생성된 프로미스 객체를 반환하며, 선택적으로 연쇄에 사용할 수 있습니다.

Chaining

보통 두 개 이상의 비동기 작업을 순차적으로 실행해야 하는 상황을 흔히 보게 됩니다. 순차적으로 각각의 작업이 이전 단계 비동기 작업이 성공하고 나서 그 결과값을 이용하여 다음 비동기 작업을 실행해야 하는 경우를 의미합니다. 우리는 이런 상황에서 promise chain을 이용하여 해결하기도 합니다.

then() 함수는 새로운 promise를 반환합니다. 처음에 만들었던 promise와는 다른 새로운 promise입니다.

const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);

//또는
const promise2 = doSomething().then(successCallback, failureCallback);

 

두 번째 promise는 doSomething() 뿐만 아니라 successCallback or failureCallback 의 완료를 의미합니다. successCallback or failureCallback 또한 promise를 반환하는 비동기 함수일 수도 있습니다. 이 경우 promise2에 추가된 콜백은 successCallback또는 failureCallback에 의해 반환된 promise 뒤에 대기합니다.

기본적으로, 각각의 promise는 체인 안에서 서로 다른 비동기 단계의 완료를 나타냅니다.

예전에는 여러 비동기 작업을 연속적으로 수행하면 고전적인 '지옥의 콜백 피라미드'가 만들어 졌었습니다

 

doSomething(function (result) {
  doSomethingElse(
    result,
    function (newResult) {
      doThirdThing(
        newResult,
        function (finalResult) {
          console.log("Got the final result: " + finalResult);
        },
        failureCallback,
      );
    },
    failureCallback,
  );
}, failureCallback);

 

모던한 방식으로 접근한다면, 우리는 콜백 함수들을 반환된 promise에 promise chain을 형성하도록 추가할 수 있습니다:

 

doSomething()
  .then(function (result) {
    return doSomethingElse(result);
  })
  .then(function (newResult) {
    return doThirdThing(newResult);
  })
  .then(function (finalResult) {
    console.log("Got the final result: " + finalResult);
  })
  .catch(failureCallback);


//then 에 넘겨지는 인자는 선택적(optional)입니다.
//그리고 catch(failureCallback) 는 then(null, failureCallback) 의 축약입니다.
//이 표현식을 화살표 함수로 나타내면 다음과 같습니다.

doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

Chaining after a catch

chain에서 작업이 실패한 후에도 새로운 작업을 수행하는 것이 가능하며 매우 유용합니다.

new Promise((resolve, reject) => {
  console.log("Initial");

  resolve();
})
  .then(() => {
    throw new Error("Something failed");

    console.log("Do this");
  })
  .catch(() => {
    console.log("Do that");
  })
  .then(() => {
    console.log("Do this, whatever happened before");
  });

그러면 다음 텍스트가 출력됩니다.

Initial
Do that
Do this, whatever happened before

 

종합예제

// 오류 처리를 실험하기 위해 'threshold' 값은 무작위로 오류를 발생시킵니다.
const THRESHOLD_A = 8; // 0을 사용하여 오류를 보장할 수 있습니다.

function tetheredGetNumber(resolve, reject) {
  setTimeout(() => {
    const randomInt = Date.now();
    const value = randomInt % 10;
    if (value < THRESHOLD_A) {
      resolve(value);
    } else {
      reject(`Too large: ${value}`);
    }
  }, 500);
}

function determineParity(value) {
  const isOdd = value % 2 === 1;
  return { value, isOdd };
}

function troubleWithGetNumber(reason) {
  const err = new Error("Trouble getting number", { cause: reason });
  console.error(err);
  throw err;
}

function promiseGetWord(parityInfo) {
  return new Promise((resolve, reject) => {
    const { value, isOdd } = parityInfo;
    if (value >= THRESHOLD_A - 1) {
      reject(`Still too large: ${value}`);
    } else {
      parityInfo.wordEvenOdd = isOdd ? "odd" : "even";
      resolve(parityInfo);
    }
  });
}

new Promise(tetheredGetNumber)
  .then(determineParity, troubleWithGetNumber)
  .then(promiseGetWord)
  .then((info) => {
    console.log(`Got: ${info.value}, ${info.wordEvenOdd}`);
    return info;
  })
  .catch((reason) => {
    if (reason.cause) {
      console.error("Had previously handled error");
    } else {
      console.error(`Trouble with promiseGetWord(): ${reason}`);
    }
  })
  .finally((info) => console.log("All done"));

//랜덤값을 뽑아서 홀짝을 판별한다.
//출력
//Got: 6, even
//All done

 

 

'Javascript' 카테고리의 다른 글

Express  (0) 2025.02.07
Arrow Function(화살표 함수)  (0) 2025.02.05
async/await  (0) 2025.02.05
var, let ,const의 차이 그리고 변수 호이스팅  (0) 2025.02.04
Promise와 forEach의 동작 차이  (0) 2024.12.31