본문 바로가기
JavaScript

비동기 실행 관련 3가지 문법 정리

by 프로랑 2023. 11. 16.
728x90

비동기 실행의 정의는 아래와 같이 다양한 방식으로 표현될 수 있습니다.

  • 특정 작업이 시작되고, 그 작업이 모두 완료되기 전에 바로 다음 코드가 실행되는 방식의 실행, 나머지 작업은 나중에 콜백을 통해 수행되는 방식의 실행
  • 특정 처리를 나중으로 미루는 방식의 실행
  • 콜백을 등록해두고, 추후에 특정 조건이 만족되면 그 콜백으로 나머지 작업을 수행하는 방식의 실행

이렇게 다양한 표현으로 그 정의를 써볼 수 있는데요.

특정 처리를 담당하는 존재(콜백)의 실행을 나중으로 미룬다는 점만 잘 기억하시면 딱히 어려울 게 없습니다.

 

비동기 실행 관련 문법 중 3가지

(1) 파라미터로 바로 콜백을 전달하는 형태의 전통적인 비동기 실행 함수

(2) Promise

(3) async/await

에 대해 정리하겠습니다.

 

1. 파라미터로 바로 콜백을 전달하는 형태의 전통적인 비동기 실행 함수

setTimeout, setInterval 함수, DOM 객체의 addEventListener 메소드 등이 여기에 해당했습니다.

setTimeout(() => {
  console.log('asynchronously executed');
}, 2000);

button.addEventListener('click', (event) => { console.log('You Clicked'); });

이렇게 함수의 파라미터로 콜백을 바로 전달하는 방식은 여전히 많은 경우에 쓰이고 있지만,

여러 비동기 작업의 순차적인(sequential) 처리가 필요한 경우에 이런 함수들로 코드를 작성하면,

fs.readFile('file1.txt', 'utf8', (error1, data1) => {
  if (error1) {
    console.log(error1);
  } else {
    console.log(data1);
    fs.readFile('file2.txt', 'utf8', (error2, data2) => {
      if (error2) {
        console.log(error2);
      } else {
        console.log(data2);
        fs.readFile('file3.txt', 'utf8', (error3, data3) => {
          if (error3) {
            console.log(error3);
          } else {
            console.log(data3);
          }
        });
      }
    });
  }
});

위와 같이 코드의 가독성이 급격하게 떨어지는 콜백 헬(callback hell) 문제가 발생할 가능성이 높다고 했습니다.

 

2. Promise

fetch('https://www.google.com')
  .then((response) => response.text())
  .then((result) => { console.log(result); })
  .catch((error) => { console.log(error); })
  .finally(() => { console.log('exit'); });

Promise 객체를 사용하면 콜백 헬 문제를 방지하면서, 여러 비동기 작업을 순차적으로 처리할 수 있습니다.

그리고 기존의 1.과 같은 전통적인 비동기 실행 함수들 중에서도 그 콜백이 단 한 번만 실행되는 함수들은

function readFile_promisified(filename) {
  const p = new Promise((resolve, reject) => {
    fs.readFile(filename, 'utf8', (error, data) => {
      if (error) {
        reject(error); // 에러 발생 시 -> rejected
      } else {
        resolve(data); // 파일 내용 읽기 완료 -> fulfilled
      }
    });
  });
  return p;
}

이런 식으로 Promisify해서 콜백 헬의 가능성을 없애고, Promise Chain 안에서 그 콜백의 리턴값을 사용할 수 있습니다.

그리고 rejected 상태의 Promise 객체에 대비하기 위한 catch 메소드,

어느 상황이든 항상 마지막에 실행해야 할 코드가 있을 때 사용하는 finally 메소드도 있습니다.

 

3. async / await 구문

async function fetchAndPrint() {
  try {
    const response = await fetch('https://www.google.www');
    const result = await response.text();
    console.log(result);
  } catch(error) {
    console.log(error);
  } finally {
    console.log('exit');
  }
}

fetchAndPrint();

async/await 구문은 Promise 객체를 다루는 코드(Promise Chaining 코드 등)를 사람들이 좀더 익숙하게 느끼는 동기 실행 스타일의 코드로 작성할 수 있게 해주는 Syntactic sugar라고 했습니다. 하지만 동기 실행 코드처럼 보인다고 실제로 코드가 보이는 순서대로 실행되는 것은 아닙니다.

 

async 함수 안의 내용이 순차적으로 실행되다가도, await 문을 만나면 await 바로 뒤에 붙은 코드를 실행해두고, 일단은 함수 바깥으로 코드 흐름이 바뀌는 부분이 중요합니다.

 

이런 점만 주의하면 사실 Promise 기반의 코드들은 가능한 경우에 모두 async/await 구문으로 전환해서 작성하는 게 더 좋습니다.

 

참고로 async/await(ES2017, 2017년 도입)Promise(ES6, 2015년 도입)에 비해 보다 더 최근에 자바스크립트 표준에 도입된 문법인데요. 이렇게 같은 기능을 더 편하게 구현할 수 있도록 자바스크립트 문법은 늘 발전하고 있습니다.

728x90

 

자바스크립트로 비동기 실행 코드를 작성할 수 있는 방법 3가지를 살펴봤습니다.

아직 위의 3가지 비동기 실행 관련 문법들은 서로 상호보완적인 것들이라고 할 수 있습니다.

왜냐하면 아직 아래와 같이 하나가 다른 하나를 완벽히 대체하지 못하는 측면이 있기 때문입니다.

  1. 콜백을 함수의 파라미터로 바로 전달하는 전통적인 방식의 비동기 실행 함수들 중에서도 setInterval, addEventListener처럼 그 콜백이 단 한번이 아니라 여러 번 실행되어야 하는 것들은 Promisify해서 사용하면 안 됩니다. Promise 객체는 한번 fulfilled 또는 rejected 상태가 되고나면 그 상태와 결과가 다시는 바뀌지 않기 때문입니다.
  2. async/await 구문의 경우, await은 async 함수 안에서만 사용할 수 있고, 코드의 top-level(어떤 함수 블록 안에 포함된 것이 아닌 코드의 최상위 영역)에서는 사용될 수는 없습니다. 그래서 코드의 top-level에서 async 함수가 리턴한 Promise 객체의 작업 성공 결과를 가져오려면 await을 사용할 수는 없고, 여전히 then 메소드를 사용해야합니다.

그러니까 이 3가지 모두 잘 알아두는 것이 좋습니다.

 

그리고 실제 개발을 할 때는 여러분이 사용하려는 문법이 어느 웹 브라우저에서 지원을 하고 안 하는지를 체크해야 하는데요. 자바스크립트의 각 문법별 브라우저 지원 현황을 이런 사이트 등을 통해 조사하고 개발을 시작하는 게 좋습니다. 다행히 이때까지 우리가 배운 3가지 종류의 문법은 대다수의 웹 브라우저에서 지원합니다.

 

사실 비동기 실행에 관해서 중요한 것은 어느 문법의 코드를 작성하든 그 개념을 제대로 이해하고 코드를 작성해야 한다는 점입니다. 콜백 헬 문제를 해결하기 위해 Promise 객체가 등장했고, Promise를 좀 더 편하게 다루기 위해서 async/await 구문이 등장했지만, 여러분이 각 문법을 제대로 이해하지 못하고 코드를 작성한다면, 그것은 또다서 Promise hell, async/await hell이 될 뿐입니다.

 

하지만 각각의 문법을 제대로 이해하고 코드를 작성한다면 개발 실력과 생산성은 한층 업그레이드될 것입니다.

728x90