두치의 개발공부

비동기 처리 본문

언어/JavaScript

비동기 처리

Du_chi 2022. 1. 9. 17:07

이번에는 자바스크립트에서 비동기 처리에 대해서 알아보도록 하겠습니다.

 

동기/비동기 란 무엇인가?

동기식(Synchrononus)

먼저 시작된 작업이 완료될 때까지 다른 작업을 시작하지 않고 기다리는 것을 말합니다.

비동기식(Asynchronous)

먼저 시작된 작업의 완료 여부와 상관 없이 새로운 작업을 시작합니다.

새로운 작업이 먼저 시작한 작업보다 먼저 완료될 수도 있습니다.

 

자바스크립트는 기본적으로 비동기적으로 동작을 합니다.

하지만, 어떤 일을 하다보면 선행되고 있는 작업이 완료된 후에 진행돼야 하는 일이 있습니다.

예를 들면 바지를 입고 신발을 신어야 하는 상황입니다. 바지를 입으면서 신발을 신을 수는 없으니까요!

 

이와 같이 자바스크립트에서 비동기 방식의 동작을 동기식으로 바꾸는 것을 비동기 처리 라고 합니다.

 

비동기 처리 방법

자바스크립트에서 비동기 처리 방법은 크게 3가지 방법이 있습니다.

  1. 콜백 함수
  2. Promise
  3. async/await

1->2->3 순으로 발전하였으며, 최근에는 3번의 방법으로 비동기 처리를 많이 하고 있습니다.

순서대로 어떻게 변하였는지 확인해 보도록 하겠습니다.

 

1. 콜백 함수

콜백함수는 파라미터로 함수를 전달받아서 내부에서 실행시키는 것을 말합니다.

 

● 콜백함수를 사용하지 않은 경우

function callName(name) {
  console.log(name);
}

function callHello() {
  let value;

  console.log("Hi My Name is");
  console.log("Uhmmmm......");

  setTimeout(function () {
    value = "Duchi";
  }, 3000);

  return value;
}


const myName = callHello();
callName(myName);

이름을 3초 후에 출력하려고 하지만,  value라는 변수에 'Duchi'가 할당되기도 전에 callName 함수가 실행되어 undefined 가 출력되었습니다.

 

● 콜백 함수를 사용 한 경우

function callName(name) {
  console.log(name);
}

function callHello() {
  let value;

  console.log("Hi My Name is");
  console.log("Uhmmmm......");

  setTimeout(function () {
    value = "Duchi";
    return callName(value);
  }, 3000);
}

const myName = callHello();

3초간 대기 후에 콜백 함수로 전달된 callName 함수가 실행되어 'Duchi'를 출력하게 됩니다.

 

이러한 콜백 함수에는 큰 단점이 하나 있습니다.

여러 가지 작업이 연결된다면 매우 복잡 해 지고 읽기조차 쉽지 않게 됩니다. 흔히들 콜백 지옥이라고 부르는 상황입니다.

function goWork(time1, timeStartWork) {
  wakeUp(time1, function (time2) {
    takeSubway(time2, function (time3) {
      takeOffSubway(time3, function (time4) {
        arriveWork(time4, function (arrivalTime) {
          console.log(timeStartWork, arrivalTime);
        });
      });
    });
  });
}

 

이러한 콜백 지옥에서 구원해 줄 Promise 가 등장 하였습니다.

 

2. Promise

비동기 처리 함수에서 처리된 결과값을 반환할 경우, 함수에서 찾을 수밖에 없어 코드가 복잡해지는 경우가 있습니다. 이에 비해 Promise 구조는 비교적 간단하여 반환 값을 찾아 사용하기 쉽습니다.

출처 : MDN WEB DOCS

  • 특징
    어떤 작업이 성공했을 때(resolve), promise 객체의 then() 함수에 넘겨진 파라미터(함수)를 단 한번만 호출하겠다는 약속입니다.
    실패했을 경우(reject) 경우에도 catch() 함수를 통해서 실패 이후의 작업을 처리할 수 있습니다.
  • Promise 3가지 상태
    1. pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
    아래와 같이 new Promise() 메서드를 호출하면 Pending(대기) 상태가 됩니다.
new Promise();

     2. fulfilled(이행) : 비동기 처리가 완료되어 Promise 결과 값을 반환해준 상태

        콜백 함수의 인자 resolve를 아래와 같이 실행하면 fulfilled(이행) 상태가 됩니다.

        이행 상태가 되면 애라와 같이 then() 함수를 이용하여 처리 결과 값을 받을 수 있습니다.

function getName() {
  return new Promise(function (resolve, reject) {
    const name = "Duchi";
    resolve(name);
  });
}

getName().then(function (resolvedData) {
  console.log(resolvedData); // Duchi
});

    3. reject(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

       실패 상태가 되면 에러를 catch() 함수를 이용하여 받을 수 있습니다.

function getName() {
  return new Promise(function (resolve, reject) {
    reject(new Error("Worng Name!!"));
  });
}

getName()
  .then()
  .catch(function (error) {
    console.log(error); // Wrong Name!!
  });

Promise를 쓰면 콜백 지옥에서 탈출 하고 좀 더 간결하게 코드를 작성할 수 있다고 하였는데, 위의 콜백지옥 코드를 Promise를 사용하여 리팩토링 해보도록 하겠습니다. 코드의 간결화를 강조할 수 있도록 ES6문법을 적용하여 작성하였습니다.

const wakeUp = (time) =>
  new Promise((resolve, reject) => {
    resolve(time);
  });

const takeSubway = (time) =>
  new Promise((resolve, reject) => {
    resolve(time);
  });
  
  .
  .
  .
  
const goWork = (time1, timeStartWork) => {
  return wakeUp(time1)
    .then((time2) => takeSubway(time2))
    .then((time3) => takeOffSubway(time3))
    .then((time4) => arriveWork(time4))
    .then((arrivalTime) => {
      console.log(timeStartWork, arrivalTime);
    });
};

 

3. async/await

ES6에 생긴 async/await을 사용하면 기존의 Promise를 더욱 간결하게 사용할 수 있습니다.

완전히 새로운 기능은 아니고, 기존의 Promise 객체를 기반으로 사용하여 Promise를 새로운 방식으로 사용하는 개념입니다.

Promise에서 사용하였던 resovle, reject, then, catch와 같은 문법을 사용하지 않고 오로지 async/await 만으로 코딩이 가능합니다.

const callName = async (name) => {
  return name;
};

console.log(callName("Duchi"));

그 이유는 함수에 async 만 붙이면 반환 값이 Promise 객체로 반환되기 때문입니다.

 

다음은 await을 이용하여 Promise 객체를 어떻게 접근하는지 확인해 보겠습니다.

const callName = async (name) => {
  console.log(name);
};
const callHello = async () => {
  let value;

  console.log("Hi My Name is");
  console.log("Uhmmmm......");
  value = "Duchi";
  return await callName(value);
};

const myName = callHello();

이렇게 비동기 처리를 매우 간단하게 처리할 수 있습니다.

그러면 결과값은 받아오는 것을 확인하였으니(then, resolve) 에러를 처리하는 방법을 확인해 보겠습니다.

 

async/await 에서는 try/catch문을 통해 에러를 처리할 수 있습니다.

반면,  Promise의 경우 promise내부적으로 발생한 에러가 catch 되지 않아 중복된 에러 처리 코드를 작성해야 합니다.

 

  • Promise에서 try/catch 사용 시
const getData = () =>
  new Promise((resolve, reject) => {
    const result = "SUCCESS";
    resolve(result);
  });

const promiseTest = () => {
  try {
    getData().then((resolvedData) => {
      throw new Error("Error Handling Test");
    });
  } catch (error) {
    console.log("Catch Error!!!!");
    console.log(error);
  }
};

promiseTest();

catch 문의 'Catch Error!!!!' 이 찍히지 않았습니다.

  • async/await 에서 try/catch 사용 시
const getData = () =>
  new Promise((resolve, reject) => {
    const result = "SUCCESS";
    resolve(result);
  });

const asyncAwaitTest = async () => {
  try {
    const result = await getData();
    throw new Error("Error Handling Test");
  } catch (error) {
    console.log("Catch Error!!!!");
    console.log(error);
  }
};

asyncAwaitTest();

catch 문의 'Catch Error' 이 출력되었습니다. 이렇게 async/awiat에서는 에러 처리가 promise에서 보다 쉽게 가능합니다.

'언어 > JavaScript' 카테고리의 다른 글

클로저  (0) 2022.01.09
setTimeout vs setInterval  (0) 2022.01.09
유사배열  (0) 2022.01.09
함수 선언식 vs 함수 표현식  (0) 2022.01.09
Data Type  (0) 2022.01.09