비동기처리와 콜백함수, promise, async/await

SangBooom·2021년 6월 15일
1

비동기처리와 콜백함수, promise, async/await에 대해 간단하게 정리해보자.

비동기 처리

특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것이 비동기 처리이다.

만약 동기적으로만 코드가 실행된다면, 앞에서 실행된 코드가 모두 실행되는 것을 기다리고 다음 코드가 실행되기 때문에
유저에게 좋지 못한 사용자 경험을 제공하게 된다.

결국 싱글 쓰레드인 자바스크립트에서 좋은 사용자 경험과 성능적으로 유리하게 이끌어가기 위해서 비동기 처리라는 방식을 채택하고 있다고 볼 수 있습니다.

비동기 처리의 문제점

개발자는 자바스크립트 엔진이 아니기 때문에 순서가 뒤죽박죽인 코드의 실행이 직관적이지 않아 불편할 수도 있고

무엇보다 api call과 같이 서버에 보낸 요청에 대한 응답이 오고 그 응답을 통해 다음 코드가 실행되어야 하는 경우엔 동기적으로 실행되도록 처리를 해 주어야 된다.

콜백함수

함수의 인자로 함수를 넘겨줌으로써 제어권(실행 시점)을 넘겨주는 함수이다.

콜백함수의 문제점

그렇게 되면 개발자는 코드가 실행 되고 결과를 반환할 때까지 수동적인 자세로 기다려야 한다.

그리고 콜백 함수가 깊어지게 되면 가독성이 매우 떨어진다. 이러한 문제점들을 보완하기 위해 Promise라는 객체를 사용한다.

Promise

비동기 처리의 결과로 나오는 객체이다. 그 결과를 성공적으로 받았을때(resolve)와 결과 받는 것을 실패했을 때(reject)로 나눠서 에러처리가 가능하다.

Promise를 이용함으로써 코드를 좀 더 직관적으로 바꿀 수 있었고, .then() 과 .catch() 덕분에 에러 핸들링도 수월해졌다.

Promise의 아쉬운점

하지만 .then()이 여러번 연결된 경우, Promise의 비동기 처리에선 .then()의 스코프 내에서 코드를 작성해야 하기 때문에 여전히 가독성에서 문제가 생기게 된다.

위의 Promise 객체를 만들어서 resolve(), reject(), .then(), .catch() 를 사용해야만 비동기 처리가 가능하다는 것이 여전히 좀 아쉽다.

만약 .then() 이 계속되어 프로미스 체이닝이 된다면 가독성이 그렇게 좋지는 않을 것이다.

async/await

async 가 나오면서 new Promise() 생성자를 통해 Promise를 만들어 줄 필요가 없어졌고, async 키워드만 붙여주면 해당 함수에서 반환되는 값이 자동으로 Promise 객체가 된다.

그리고 resolve로 넘겨준 값을 .then()을 사용해 받는 거 대신 await 키워드를 사용하면 된다.

await는 async 함수 내의 Promise 앞에 쓰이면 async 함수의 실행을 일시 중지하고 Promise 해결을 기다린 다음 함수의 실행을 재개 한다는 것이다.

async/await을 이용한 에러처리

async/await의 가장 큰 특징은 콜백, .then()과 .catch()를 이용하는 특정한 문법을 이용하는게 아니라 일반적인 동기 코드를 짜는 것과 같은 방식으로 짤 수 있다는 점이다.

따라서, 에러처리도 일반적인 try/catch 문을 통해서 할 수 있는 것이다.

async/await 연습 예제

#1

const sample1 = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('sample');
        },[1000])
    })
}
const sampleAsync2 = async () => {
    const result = await sample1();
    return result; // async 함수에서 return 하게되면 무조건 Promise 객체가 반환된다.
}
sampleAsync2().then((result) => console.log(result)); // 그렇기 때문에 외부에서 sample 문자열을 얻기 위해서는 then 체이닝을 통해 얻어와야한다. 

// 아니면 아래와 같이 console.log 부분과 같이 값을 전달받아 처리하면된다.
const sampleAsync2 = async () => {
    const result = await sample1();
    console.log(result)
}
sampleAsync2();

#2

console.log(1);

const promiseOne = function () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(3);
      resolve("one");
    }, 3000);
  });
};

const promiseTwo = function () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("two");
    }, 1000);
  });
};

console.log(2);

async function foo() {
  const result = await promiseOne(); // 프라미스가 이행될 때까지 아래 코드로 넘어가지 않음..
  const resultTwo = await promiseTwo(); // 위 코드의 프로미스가 반환될 때까지 대기...

  console.log(resultTwo); // 완료 되면 하단의 코드가 이어서 실행됨

  const parellOne = promiseOne(); // 위 아래 타이머는 동시에 시작됨.
  const parelltwo = promiseTwo(); // 해당 프로미스 이행 값이 먼저 반환됨.(약 1초)

  console.log(await parellOne);
  console.log(await parelltwo); // 먼저 프로미스 객체가 반환되었지만 위 함수가 먼저 실행되어야 실행됨.
}

foo();

복습하고 또 복습하자.

출처 :

profile
끊임없이 떨어지는 물방울이 바위를 뚫는다

0개의 댓글