이끌든지 따르든지 비키든지

Software Development/JavaScript

[JavaScript] 자바스크립트의 비동기 처리

SeongHo5 2023. 9. 18. 19:35

비동기(Asynchronous) 처리

자바스크립트는 단일 스레드 기반의 언어로, 한 번에 하나의 작업만 처리할 수 있다. 그러나 비동기 처리 방식을 사용해 코드를 순차적으로 실행하되, 실행 결과를 기다리지 않고 다음 코드를 실행한다. 그래서, 우리가 보기에 여러 작업이 동시에 실행되는 것처럼 보인다.

비동기 처리는 코드를 순차적으로 실행하지만, 실행 결과를 기다리지 않고 다음 코드를 실행한다. 이 때문에, 작업이 실행 중일 때 다른 작업을 수행할 수 있으며, 사용자의 요청에 빠르게 응답할 수 있다.

 

자바스크립트의 비동기 처리를 확인할 수 있는 간단한 예시를 보자면,

 

console.log("Hello");

setTimeout(function() { // 1초 뒤에 실행
    console.log("Dev"); 
}, 1000);

console.log("World");

 

이 코드는 콘솔에 문자열을 출력하는 일반적인 코드이다.

 

"Hello"
"Dev"
"World"

 

코드는 순차적으로 실행되니 위와 같이 출력될 것으로 예상했지만...

 

실제 실행 결과는 아래와 같다.

 

"Hello"
"World"
"Dev"

 

출력이 이렇게 되는 이유는, setTimeout 함수를 실행하고, 1초 뒤 실행되는 결과를 기다리지 않고, 다음 코드인 console.log("World")를 실행하기 때문이다.

 

자바스크립트는 동기 처리가 필요한 코드를 실행하기 위해 callback, Promise, async / await 를 사용해 동기 처리를 구현한다.


callback() 함수

function doAsyncTask(callback) {
  console.log('비동기 작업 시작');
  setTimeout(function() {
    console.log('비동기 작업 완료');
    callback(); // 비동기 작업 완료 후 콜백 호출
  }, 2000); // 2초 후에 작업이 완료됨
}

function callbackFunction() {
  console.log('콜백 실행됨');
}

// doAsyncTask 함수 호출, 콜백 함수 전달
doAsyncTask(callbackFunction);

이렇게 콜백함수를 작성하면,

" 비동기 작업 시작 → 비동기 작업 완료 → 콜백 실행됨 " 순으로 코드가 실행되어, 비동기 처리를 구현할 수 있다.

 

callback 함수는 간결한 코드로 비동기 처리를 구현할 수 있지만, 여러 작업을 연속으로 처리할 때 치명적인 단점이 있다.

function step1(callback) {
  setTimeout(function() {
    console.log('Step 1 completed');
    callback();
  }, 1000);
}

function step2(callback) {
  setTimeout(function() {
    console.log('Step 2 completed');
    callback();
  }, 1000);
}

function step3(callback) {
  setTimeout(function() {
    console.log('Step 3 completed');
    callback();
  }, 1000);
}

step1(function() {
  step2(function() {
    step3(function() {
      console.log('All steps completed');
    });
  });
});

이렇게 콜백함수가 중첩되다 보면 코드가 복잡해져 오류 제어, 흐름 제어, 가독성 무엇 하나 컨트롤할 수 없는 상황이 된다.

 

그래서 callback 함수를 대체하는 promise라는 비동기 처리를 효율적으로 다루는 새로운 객체를 사용한다.


Promise

promise는 비동기 작업의 성공, 실패와 그 결과 값을 나타내는 객체이다.

pending, fullfilled, rejected의 3가지 상태를 가질 수 있는데, 예시 코드를 통해 알아보면, 

const myPromise = new Promise((resolve, reject) => {
  // 비동기 작업 수행
  if (/* 작업 성공 */) {
    resolve('작업 성공'); // 작업이 성공적으로 완료됨
  } else {
    reject('작업 실패'); // 작업이 실패함
  }
});

// Promise 사용
myPromise
  .then((successMessage) => {
    console.log('성공: ' + successMessage);
  })
  .catch((errorMessage) => {
    console.log('실패: ' + errorMessage);
  });

 

new Promise로 새로운 객체를 생성해 작업 결과에 따라 성공(resolve),  실패(reject) 메시지를 반환하는데, 

이 메시지에 따라 성공, 실패 시의 동작을 정의하고 실행해 비동기 작업을 구현한다.

 

promise를 사용하면 callback 함수의 콜백 지옥 없이 훨씬 간결하고, 효율적으로 비동기 처리를 구현할 수 있다.

 


async / await

async / await는 ES2017(ES8)부터 도입된 새로운 키워드인데, promise를 기반으로 비동기 처리를 좀 더 간편하게, 동기적으로 구현할 수 있도록 해준다.

 

async function asyncFunction() {
  console.log('시작');
  await delay(2000); // 2초 동안 대기
  console.log('2초 후에 실행됨');
  return '완료';
}

asyncFunction()
  .then(result => console.log('결과:', result))
  .catch(error => console.error('에러:', error));

함수 선언 시 async 키워드를 사용하는 것으로 async 객체로 선언할 수 있으며, async 함수 내에서 await로 선언된 메서드는 동기 방식(실행 결과를 기다림)으로 처리된다.

 

async / await로 선언된 객체에도 .then() / .catch()를 통해 성공  ·  실패 시의 작업을 정의 · 실행할 수 있다.