본문 바로가기

Codeit Frontend PB

JavaScript: 비동기 실행과 Promise

동기(Synchronous) 프로그래밍 vs 비동기(Asynchronous) 프로그래밍

 

동기 코드는 연산이 완료될 때 까지 기다린 후, 다음 코드라인을 실행한다.

즉, 다음 코드 라인은 연산이 완전히 완료된 후에만 실행된다.

각 연산이 순서대로 실행되기 때문에 코드의 흐름을 이해하기 쉽다는 장점이 있지만

대용량 파일 처리, 웹서버 요청, 소켓 통신 같이 시간이 오래걸리는 특정 작업의경우 전체 시스템이 차단될 수 있다는 단점이 있다.

 

비동기 코드는 현재 연산이 완료되지 않더라도 request만 보내놓고, 다음 코드 라인을 실행할 수 있다.

I/O 작업, 네트워크 호출 같이 시간이 많이 소요되는 작업 수행 시 비동기 프로그램이 프로그램의 효율을 매우 높여준다.

복잡한 작업을 background에서 수행하고, 작업이 완료되면 callback, promise, async/await 등을 사용해 결과를 처리할 수 있다.

 

 

 

비동기 프로그래밍의 필요성

현대의 웹 애플리케이션은 사용자 요청을 신속하게 처리하면서 동시에 UI 응답성을 유지해야 한다.

비동기 프로그래밍은 네트워크 요청이나 파일 I/O, 데이터베이스 작업과 같이 시잔이 걸리는 작업들을 백그라운드에서 처리하여, 메인 실행 흐름을 방해하지 않고 결과를 처리할 수 있도록 한다.

 

 

 JavaScript와 Promise

 

Promise - JavaScript | MDN

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

developer.mozilla.org

 

JavaScript에서 비동기 작업을 관리하는 가장 기본적인 방법 중 하나는 Promise 객체를 사용하는 것이다.

프로미스는 비동기 작업의 최종 완료 또는 실패를 나타내는 일종의 대기중인 값이다.

 

Promise의 상태
Pending 초기 상태, 성공 또는 실패가 결정되지 않음.
Fulfilled 연산이 성공적으로 완료됨
Rejected 연산이 실패함

 

 

Promise Chaining

프로미스 체이닝은 여러 비동기 작업을 연결해 각 작업의 결과가 다음 작업의 입력으로 전달되도록 하는 기법이다.

이를 통해 복잡한 비동기 로직을 깔끔하게 순차적으로 표현할 수 있다.

 

동작 방식에 대한 예시는 다음과 같다.

 

아래 코드는...

✪ 비동기작업을 Promise 객체로 처리해 각 작업이 완료되기 전까지 메인 스레드를 블로킹하지 않고 다른 작업을 계속할 수 있다. 그리고 Promise 객체는 비동기 작업의 상태(대기, 이행, 거부)를 관리하기 때문에 각 작업이 완료되었는지 여부를 쉽게 확인할 수 있게 한다. 

 

✪ downloadFile, readFile, modifyFile 이 세가지 함수가 Promise 객체를 반환하면서 이를 체이닝하여 순차적으로 비동기 작업을 처리한다. 즉, 프로미스 체이닝을 통해 각 작업이 완료된 후에 다음 작업을 실행할 수 있도록 보장한다.

프로미스 체이닝은 callback 지옥을 방지하고 비동기작업의 흐름을 더 명확하게 관리할 수 있게 한다. 작업이 순차적으로

실행되고 중간에 오류가 발생할 경우 'catch' 블록에서 처리해준다.

 

 

Callback 지옥?

1) 안좋은 예

asyncFunction1(function(result1) {
  asyncFunction2(result1, function(result2) {
    asyncFunction3(result2, function(result3) {
      asyncFunction4(result3, function(result4) {
        // 결과 처리
      });
    });
  });
});

 

2) 좋은 예

asyncFunction1()
  .then(result1 => asyncFunction2(result1))
  .then(result2 => asyncFunction3(result2))
  .then(result3 => asyncFunction4(result3))
  .then(result4 => {
    // 결과 처리
  })
  .catch(error => {
    // 에러 처리
  });

 

downloadFile 함수는 파일을 다운로드하고, 성공적으로 다운로드되면 다운로드 경로를 반환(reslove)한다.

그 후 반환된 경로는 readFile 함수에 전달되며, 이 함수는 해당 파일을 읽고 내용을 반환한다(파일 내용 resolve).

읽은 내용은 modifyFile 함수로 전달되어 수정되고, 수정된 내용이 반환된다(수정된 내용을 resolve).

// 첫 번째 프로미스: 파일 다운로드
function downloadFile(url) {
  return new Promise((resolve, reject) => {
    console.log(`Downloading file from ${url}`);
    // 시뮬레이션을 위한 타이머
    setTimeout(() => {
      const filePath = 'local/path/to/file';
      console.log(`Downloaded file to ${filePath}`);
      resolve(filePath); // 파일 경로를 결과로 반환
    }, 2000);
  });
}

// 두 번째 프로미스: 파일 읽기
function readFile(path) {
  return new Promise((resolve, reject) => {
    console.log(`Reading file at ${path}`);
    // 파일 읽기 시뮬레이션
    setTimeout(() => {
      const fileContents = 'File contents';
      console.log(`Read file: ${fileContents}`);
      resolve(fileContents); // 파일 내용을 결과로 반환
    }, 1500);
  });
}

// 세 번째 프로미스: 파일 내용 수정
function modifyFile(contents) {
  return new Promise((resolve, reject) => {
    console.log(`Modifying file contents`);
    // 수정 작업 시뮬레이션
    setTimeout(() => {
      const newContents = contents + ' modified';
      console.log(`Modified file contents: ${newContents}`);
      resolve(newContents);
    }, 1000);
  });
}

// 프로미스 체이닝 사용
downloadFile('http://example.com/file')
  .then(readFile)
  .then(modifyFile)
  .then((modifiedContents) => {
    console.log(`Final modified contents: ${modifiedContents}`);
  })
  .catch((error) => {
    console.error(`An error occurred: ${error}`);
  });

 

async/await의 편리성

async/await 구문은 프로미스를 더 쉽게 작성하고 읽을 수 있도록 해주는 ES8 문법적 특징이다.

async 함수 안에서 await 키워드를 사용하면 JavaScript 엔진이 프로미스가 해결될 때까지 함수 실행을 자동으로 일시중지한다.

// async/await를 사용한 비동기 함수
async function processFile(url) {
  try {
    const filePath = await downloadFile(url);
    const fileContents = await readFile(filePath);
    const modifiedContents = await modifyFile(fileContents);
    console.log(`Final modified contents: ${modifiedContents}`);
  } catch (error) {
    console.error(`An error occurred: ${error}`);
  }
}

// 비동기 함수 호출
processFile('http://example.com/file');

 

위 코드에서 await 키워드는 downloadFile, readFile, modifyFile 함수가 반환하는 Promise가 해결될 때까지 함수 실행을 일시 중지하고, 그 결과를 변수에 할당한다. 각 비동기 작업이 완료될 때까지 기다린 후 다음 작업을 수행하게 한다.

 

 

 

✔비동기 프로그래밍에서의 에러 처리

비동기 프로그래밍의 프로미스 체인에서는 .catch() 메소드를 사용해 발생할 수 있는 오류를 적절히 관리해줘야 한다.

// 비동기 함수 정의
async function fetchData(url) {
  try {
    // await 키워드를 사용하여 fetch 요청의 Promise가 해결될 때까지 기다리는 부분
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json(); 
    return data; 
  } catch (error) {
    console.error("Error fetching data:", error);
  }
}

// 함수 사용
const apiURL = "https://api.example.com/data";
fetchData(apiURL).then(data => {
  console.log("Received data:", data);
});

 

 

 

Axios를 사용한 비동기 HTTP 요청

fetch API 외에도 axios 라이브러리를 이용해 HTTP reqeust를 수행할 수 있다.

axios는 자동으로 JSON 데이터 변환, 요청 취소, HTTP 요청에 대한 광범위한 설정 옵션을 제공한다.

위 fetch 메소드를 이용한 비동기 처리를 axios를 이용한 요청으로 바꾸게 되면 아래와 같다.

 

axios의 장점!!

👏 axios는 response.status가 200 범위가 아니면 자동으로 에러를 throw 하므로 별도의 상태 코드 체크가 불필요하다.

👏 axios는 자동으로 JSON 데이터를 파싱하여 반환한다.

👏axios는 네트워크 에러뿐만 아니라 HTTP 상태 코드 에러도 catch 블록에서 잡을 수 있다.

 

axios

Promise based HTTP client for the browser and node.js. Latest version: 1.7.2, last published: 2 months ago. Start using axios in your project by running `npm i axios`. There are 140776 other projects in the npm registry using axios.

www.npmjs.com

// 비동기 함수 정의
async function fetchData(url) {
  try {.
    const response = await axios.get(url);
    return response.data; 
  } catch (error) {
    console.error("Error fetching data:", error.response ? error.response.data : error.message);
  }
}

// 함수 사용
const apiURL = "https://api.example.com/data";
fetchData(apiURL).then(data => {
  console.log("Received data:", data);
});

'Codeit Frontend PB' 카테고리의 다른 글

GIT 기본 명령어 정리 - 1  (0) 2024.08.05
Web API: Pagination  (0) 2024.07.21
Cookie, sessionStorage, localStorage에 관하여  (0) 2024.07.13