Promise

송은·2023년 6월 12일
0
post-thumbnail

프로미스는 비동기 작업을 조금 더 편하게 처리할 수 있도록 ES6에 도입된 기능이다.
이전에는 비동기 작업을 처리할 때 콜백함수로 처리했어야 했는데, 비동기 작업이 많아질 경우 코드가 쉽게 난잡해지게 되었다.

예) 숫자 n을 파라미터로 받아와서 다섯번에 걸쳐 1초마다 1씩 더해서 출력하는 작업을 setTimeout으로 구현

function increaseAndPrint(n, callback) {
  setTimeout(() => {
    const increased = n + 1;
    console.log(increased);
    if (callback) {
      callback(increased);
    }
  }, 1000);
}

increaseAndPrint(0, n => {
  increaseAndPrint(n, n => {
    increaseAndPrint(n, n => {
      increaseAndPrint(n, n => {
        increaseAndPrint(n, n => {
          console.log('끝!');
        });
      });
    });
  });
});

이런 식으로 코드가 많아지면서 읽기 복잡해진다. 이런 식의 코드를 콜백지옥(Callback Hell)이라고 부른다..

비동기적으로 처리해야하는 일이 많아질수록, 코드의 깊이가 계속 깊어지는 현상이 있다.

하지만 Promise를 사용하면 이렇게 코드의 깊이가 깊어지는 현상을 방지할 수 있다.


Promise 사용법

const myPromise = new Promise((resolve, reject) => {
  // 구현..
})

Promise를 만들때에는 resolvereject를 파라미터로 받아오는 함수를 인자로 넣어주어야 한다.

성공할 때에는 resolve를 호출해주고, 실패할 때에는 reject를 호출해주면 된다.

다만, reject는 사용하지 않는다면 생략할 수 있다.


1초 뒤에 성공시키는 상황에 대해서 구현하였다.

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

myPromise.then(n => {
  console.log(n);
});

resolve를 호출 할 때 특정값을 파라미터로 넣어주면, 이 값을 작업이 끝나고나서 사용할 수 있다.

Promise 작업이 끝나고 나서 또 다른 작업을 해야할 때에는 Promise 뒤에 .then(...) 을 붙여서 사용하면 된다.


이번에는 1초 뒤에 실패되게끔 구현해보자.

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error());
  }, 1000) // 1초뒤에 에러를 발생시킴
});

myPromise.then(result => {
  console.log(result);
}).catch(e => {
  console.error(e);
})

Promise의 catch 함수는 앞서 만든 Promise에서 reject 한 값을 받아올 때 사용한다.

여기서 ereject에 인자로 넣어준 값이다.

실패하는 상황에서는 reject를 사용하고, .catch 를 통해서 실패했을 때 실행될 작업을 설정할 수 있다.

console.log와 달리 console.error는 빨간색으로 나와, 콘솔에서 에러가난 것을 더욱 쉽게 확인할 수 있게 해준다.


Promise 함수를 만들어보자.

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const value = n + 1;
      if (value === 5) {
        const error = new Error(); // value가 5가 됐을 때 에러를 발생시킴 (실패 상황)
        error.name = 'ValueIsFiveError'; // 에러이름을 설정함
        reject(error);
        return; // 작업 종료
      }
      console.log(value); // 실패하는 상황이 아닌 경우
      resolve(value);
    }, 1000) // 1초 뒤에 함수가 실행됨
  })
}

increaseAndPrint(0).then(n => {
  console.log('result: ', n);
})
// >> 1
// >> result: 1

increaseAndPrint 함수를 실행시켰을 때

  • 1초 뒤에

  • 파라미터에 받아온 n 값에 1을 더해주고

  • console.log(value)를 통해 value 값을 출력해준 뒤,

  • resolve 를 통해서 1을 더해준 값을 반환해준다. >>> 1

  • Promise 작업이 끝난 뒤 .then이 실행된다. >>> result: 1


여기까지만 보면, 결국 함수를 전달하는 것인데 차이점을 못느낄 수 있다.

Promise 속성 중에는 .then 내부에 넣은 함수에서 또 Promise를 리턴하게 된다면, 연달아서 사용할 수 있다.

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const value = n + 1;
      if (value === 5) {
        const error = new Error();
        error.name = 'ValueIsFiveError';
        reject(error);
        return;
      }
      console.log(value);
      resolve(value);
    }, 1000);
  });
}

increaseAndPrint(0)
  .then(n => {
    return increaseAndPrint(n);
  })
  .then(n => {
    return increaseAndPrint(n);
  })
  .then(n => {
    return increaseAndPrint(n);
  })
  .then(n => {
    return increaseAndPrint(n);
  })
  .then(n => {
    return increaseAndPrint(n);
  })
  .catch(e => {
    console.error(e);
  });

then 연속사용 결과

위 코드를 아래와 같이 정리할 수 있다.

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const value = n + 1;
      if (value === 5) {
        const error = new Error();
        error.name = 'ValueIsFiveError';
        reject(error);
        return;
      }
      console.log(value);
      resolve(value);
    }, 1000);
  });
}

increaseAndPrint(0)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .catch(e => {
    console.error(e);
  });

Promise를 사용하면, 비동기 작업의 개수가 많아져도 코드의 개수가 깊어지지 않게된다.


🚨 단점

  • 에러를 잡을 때 몇 번째에서 발생했는지 알아내기 어렵다.
  • 특정 조건에 따라 분기를 나누는 작업이 어렵다.
  • 특정 값을 공유해가면서 작업을 처리하기 까다롭다.
    👉 이 문제점들은 async/await을 사용해서 해결할 수 있다!
profile
개발자

0개의 댓글