하루에 한 문제

Promise 본문

Dev/JavaScript

Promise

dkwjdi 2021. 4. 19. 22:38

Promise란?

Promise는 자바스크립트 비동기 처리에 사용되는 객체다.

 

Promise의 3가지 상태(states)

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 Promise가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

Pending(대기)

먼저 아래와 같이 new Promise()메서드를 호출하면 대기(Pending)상태가 된다.

new Promise();

  • Promise의 매개변수는 resolve, reject라고 하는 콜백함수를 가진다.
  • 또한 반환 결과는 Promise이다!
new Promise(function(resolve, reject) {
  // ...
});

 

Fulfilled(이행)

new Promise(function(resolve, reject) {
  resolve();
});

그리고 이행 상태가되면 아래와 같이 then() 을 이용해 resolve에서 넘겨준 매개변수를 받아서 사용할 수 있다.

function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

  • then은 다시 Promise를 반환한다!
  • 그렇기 때문에 then(~~).catch(~~).finally(~~) 와 같은 체이닝이 가능한 것이다!

 

Rejected(실패)

new Promise()Promise 객체를 생성하면 콜백 함수 인자로 resolve reject를 사용할 수 있다고 했는데. 여기서 reject를 아래와 같이 호출하면 실패(Rejected) 상태가 된다.

new Promise(function(resolve, reject) {
  reject();
});
  • 실패 상태가 되면 실패한 이유를 catch()를 이용해 받을 수 있다.
  • 역시 catch또한 다시 Promise를 반환한다.

function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function(err) {
  console.log(err); // Error: Request is failed
});

 

 

 

 

Promise Chaining

function getData() {
  return new Promise({
    // ...
  });
}

// then() 으로 여러 개의 Promise를 연결한 형식
getData()
  .then(function(data) {
    // ...
  })
  .then(function() {
    // ...
  })
  .then(function() {
    // ...
  });

위에서 말했듯이 then, catch등의 return 값이 Promise이기 때문에 이러한 체이닝이 가능하다.

 

 

실무에서 있을 법한 연결 사례

getData(userInfo)
  .then(parseValue)
  .then(auth)
  .then(diaplay);
  • 위 코드는 페이지에 입력된 사용자 정보를 받아와 파싱, 인증 등의 작업을 거치는 코드를 나타낸다.
  • 아래의 코드와 같이 userInfo는 사용자 정보가 담긴 객체를 의미하고, parseValue, auth, display는 각각 Promise를 반환해주는 함수이다.
var userInfo = {
  id: 'test@abc.com',
  pw: '****'
};

function parseValue() {
  return new Promise({
    // ...
  });
}
function auth() {
  return new Promise({
    // ...
  });
}
function display() {
  return new Promise({
    // ...
  });
}

 

 

Promise 에러 처리 방법

에러 처리 방법에는 다음과 같이 2가지 방법이 있습니다.


1.then()의 두 번째 인자로 에러를 처리하는 방법

getData().then(
  handleSuccess,
  handleError
);

 

2.catch()를 이용하는 방법

getData().then().catch();

 

  • 위 2가지 방법 모두 Promise reject() 메서드가 호출되어 실패 상태가 된 경우에 된다.
  • Promise의 로직이 정상적으로 돌아가지 않는 경우 호출된다.
function getData() {
  return new Promise(function(resolve, reject) {
    reject('failed');
  });
}

// 1. then()의 두 번째 인자로 에러를 처리하는 코드
getData().then(function() {
  // ...
}, function(err) {
  console.log(err);
});

// 2. catch()로 에러를 처리하는 코드
getData().then().catch(function(err) {
  console.log(err);
});

 

하지만 Promise의 에러 처리에는 가급적 catch()를 사용하는 것이 좋다.

// then()의 두 번째 인자로는 감지하지 못하는 오류
function getData() {
  return new Promise(function(resolve, reject) {
    resolve('hi');
  });
}

getData().then(function(result) {
  console.log(result);
  throw new Error("Error in then()"); // Uncaught (in promise) Error: Error in then()
}, function(err) {
  console.log('then error : ', err);
});

getData() 함수의 Promise에서 resolve() 메서드를 호출하여 정상적으로 로직을 처리했지만, then()의 첫 번째 콜백 함수 내부에서 오류가 나는 경우 오류를 제대로 못 잡아 낸다.

하지만 똑같은 오류를 catch()로 처리하면 오류를 잡아낼 수 있다.

// catch()로 오류를 감지하는 코드
function getData() {
  return new Promise(function(resolve, reject) {
    resolve('hi');
  });
}

getData().then(function(result) {
  console.log(result); // hi
  throw new Error("Error in then()");
}).catch(function(err) {
  console.log('then error : ', err); // then error :  Error: Error in then()
});

 

 

 

 

자 이제 예를 통해 보는것이 아닌 직접 만들어서 확인해보자!

function delay(sec, callback) {
  setTimeout(() => {
    callback(new Date().toISOString());
  }, sec * 1000);
}

console.log('start');
delay(1, (result) => console.log(result));
console.log('end');
  • 이러한 delay함수가 있다고 치자.
  • 이 delay함수는 매개변수로 sec, callback을 받는다
  • setTImeout으로 sec초 후에 callback함수를 실행한다.
  • 위 코드의 실행결과는 어떻게 될까?
  • setTimeout은 비동기 함수이기 때문에 아래와 같이 나온다.

 

 

function delay(sec, callback) {
  setTimeout(() => {
    callback(new Date().toISOString());
  }, sec * 1000);
}

console.log('start');
delay(1, (result) => console.log(result));
delay(1, (result) => console.log(result));
delay(1, (result) => console.log(result));
console.log('end');
  • 위의 코드는 콘솔에 출력해보면 비동기이기 때문에 3개의 result가 거의 동시에 찍히게 된다.
  • 만약의 위의 코드를 통해 시작 시간으로부터 1초, 2초, 3초 뒤에 result를 출력하고 싶다면 어떻게 하면 될까?
console.log('start');
delay(1, (result) => {
  console.log(result);
  delay(1, (result) => {
    console.log(result);
    delay(1, (result) => {
      console.log(result);
    });
  });
});
console.log('end');
  • 위의 코드와 같이 콜백함수 안에 콜백함수를 넣어주면 된다.
  • 그런데 이러한 코드는 소위 말하는 콜백지옥을 유발할 수 있기 때문에 좋은 코드는 아니다.

 

위의 코드를 Promise를 사용해 나타내보자.

function delayP(sec) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(new Date().toISOString());
    }, sec * 1000);
  });
}

console.log('start');
delayP(1)
  .then((res) => {
    console.log(res);
    return delayP(1);
  })
  .then((res) => {
    console.log(res);
    return delayP(1);
  })
  .then((res) => {
    console.log(res);
    return delayP(1);
  });
console.log('end');

  • 우선 결과를 보면 우리가 원했던 것처럼 나온다!
  • delayP함수에서는 return으로 다시 delayP()를 넘겨주기 때문에 계속해서 하나의 비동기처리가 끝나면 다음 비동기가 처리되는 모습을 볼 수 있다.

 

참고

www.youtube.com/watch?v=CA5EDD4Hjz4

joshua1988.github.io/web-development/javascript/promise-for-beginners/#promise%EA%B0%80-%EB%AD%94%EA%B0%80%EC%9A%94

 

'Dev > JavaScript' 카테고리의 다른 글

V8 작동원리  (0) 2021.04.20
Arrow Function에서 this바인딩  (0) 2021.04.20
async VS defer  (0) 2021.04.17
NPM 이란?  (1) 2021.04.16
클로저  (0) 2021.04.15
Comments