[Java Script] 비동기와 Promise

June hyoung Park·2020년 9월 6일
0

JavaScript

목록 보기
8/18

동기와 비동기

동기적 처리는 직렬로 작업을 수행한다. 즉, 각 작업은 순차적으로 실행되며 각 작업마다 수행되고 종료될때까지 다음 작업은 대기하게 된다. 어찌보면 상당히 정돈된 수행방식이라 생각될 수 있겠지만, Ajax나 Axios를 통해 서버에 데이터를 요청하는 작업 시 서버의 응답이 얼마나 걸릴지 모르는 상황에서 서버 응답시간 동안 다음 작업은 블로킹 되기 때문에 이러한 작업을 동기적으로 처리한다면 이는 매우 비효율적이다.

그러나 이를 비동기적으로 처리한다면, 훨신 효율적으로 작업을 수행할 수 있다. 비동기 처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미하며, 비동기 처리를 수행하는 방법엔 여러가지가 있다.

콜백 함수와 콜백지옥

let number = 1;

setTimeout((_) => {
  console.log(number + "번째");
  setTimeout(() => {
    console.log(++number + "번째");
    setTimeout(() => {
      console.log(++number + "번째");
      setTimeout(() => {
        console.log(++number + "번째");
      }, 1000);
    }, 1000);
  }, 1000);
}, 1000);
/*
1번째
2번째
3번째
4번째
*/

위 예제는 자바 스크립트의 비동기 함수인 setTimeout()을 이용한 비동기 작업을 콜백 함수를 이용해서 처리한 예제이다. 물론 작동에는 이상이 없지만, 이러한 콜백 패턴은 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란해질 수 있다. 그리고 이러한 형태를 콜백 지옥이라한다.

const test = () => {
  try {
    setTimeout(() => {
      throw new Error("Error!");
    }, 1000);
  } catch (e) {
    console.log("에러가 발생하였습니다");
    console.log(e);
  }
};
test();
// Error: Error!

위 예제는 콜백함수에 대한 비동기처리시 에러처리에 대한 문제를 다룬 예제이다.

비동기 처리 함수 내의 콜백 함수는 특정 조건이 발생하면 태스트 큐(Task queue)로 이동한 후 콜 스택이 비어졌을 때, 이벤트 루프에 의해 콜 스택으로 이동되어 실행된다. setTimeout 함수가 실행된다면 내부의 throw new Error가 실행 되기전에 이미 즉시 종료되어 콜 스택에서 제거될것이며, web Api의 'tick'(카운트) 이벤트가 발생하면 throw new Error 는 콜백 큐로 이동한 후 콜 스택이 비어졌을 때 이벤트 루프에 의해 콜스택으로 이동되어 실행된다.

이때 문제는 exception의 호출자인 setTimeout은 이미 콜 스택에서 제거된 상태이기 때문에 setTimeout 함수의 콜백 함수 내에서 발생시킨 에러는 catch 블록에서 캐치되지 않은 채 프로세스는 종료된다. 그리고 이러한 문제들은 ES6부터 등장한 Promise로 해결할 수 있다.

Promise

function createPromise() {
  return new Promise((resolve, reject) => { ... } );
}

Promise는 객체는 new 생성자를 통해서 생성할 수 있는데 함수를 인자로 받으며, 이 함수 인자는 reslove와 reject를 받는다. 위 예제는 함수의 리턴값으로 Promise 객체를 반환하게끔 했다.

const testPromise = (number) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (number < 10) reject(new Error("Numbers less than 10"));
      resolve(number);
    }, 1000);
  });
};

testPromise(11)
  .then((result) => console.log(result))
  .catch((error) => console.log(error));

위 예제의 testPromise 함수는 Promise객체를 반환 후 setTimeout()에 의해 1초 뒤 넘어온 testPromise 함수의 인자인 number가 10보다 작다면 reject를 통해 Error를 전달하고 아니라면 resolve를 통해 number를 전달한다. 그 후 Promise 객체에서 리턴된 resolve는 .then reject는 .catch로 전달되어 전달된 값으로 처리할 작업을 수행할 수 있다.

Promise의 메서드 체이닝

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
})
  .then((result) => {
    console.log(result); // 1
    return result + 1;
  })
  .then((result) => {
    console.log(result); // 2
  });

위에서 언급 한 .then.catch 메서드는 새로운 Promise 객체를 반환하게 되는데, 이를 통해 여러 개의 프로미스를 연결할 수 있으며, 이를 Promise의 체이닝이라한다.

1초후 resolve(1)가 호출되면 .then()으로 넘어가게되고 받아온 result인 '1'을 출력 후 1을 더하여 다음 .then으로 넘기게된다.

Promise의 에러 처리

만일 Promise가 거부되면 제일 가까운 rejection 핸들러로 넘어가게되며,.catch를 통해 에러를 핸들링 할 수 있다.

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
})
  .then((result) => {
    throw new Error("에러 발생");
    console.log(result);
    return result + 2;
  })
  .catch((error, result) => {
    console.log(error);
  }); // Error: 에러 발생

에러가 발생한 상황을 만들기위해 .then() 안에서 throw new Error("에러 발생"); 를 통해 일부러 Error를 발생 시켰으며, 이는 .catch()로 전달되어 에러 메세지를 출력하게된다.

profile
Take me home~~~~

0개의 댓글