[Nodejs] 비동기 처리, Promise

JUJU·2024년 7월 15일
1

Nodejs

목록 보기
1/5

✏️ JavaScript의 특징

  • JavaScript는 싱글 스레드 언어이다.
    • 즉, JavaScript는 기본적으로 Synchronous 하다.

JS의 기본은 "동기"이다.

그렇다면, Ajax 요청, 이벤트 리스너, SetTimeout 등 처리하는데 시간이 오래 걸리는 작업들을 계속 기다려야 하는가?

위와 같은 작업들은 자바스크립트 엔진이 아닌, 브라우저 내부의 멀티 스레드인 Web APIs에서 비동기 + 논블로킹으로 처리된다.
➜ 비동기의 핵심 요소는 JS가 아니라 브라우저이다.


⚠️ Node.js에서는 libuv 내장 라이브러리가 처리한다.




✏️ 이벤트 루프

이벤트 루프

: 브라우저의 동작 타이밍을 제어하는 관리자
이벤트 루프를 통해서 JS 코드를 멀티스레드처럼 동작하게 만들 수 있다.


■ 구성 요소

  • Call Stack: JS 엔진이 코드 실행을 위해 사용하는 메모리 구조
  • Web APIs: 브라우저에서 제공하는 API 모음, 비동기 처리 담당
  • Callback Queue: 비동기 작업이 완료되면 실행되는 함수들이 대기하는 공간

■ 특징

  • JS 코드가 실행되면, Call Stack에 함수가 쌓인다.
  • Call Stack 이 동기적으로 작업을 처리하다가, 비동기 작업을 만나면 Web APIs로 넘긴다.
  • Call Stack 은 싱글 스레드지만, Web APIs들은 멀티 스레드이다.
    ➜ 동시 작업 처리가 가능하다.
  • 비동기 작업이 완료되면, Web API는 콜백 함수를 Callback Queue에 넣는다.

⚠️ Call Stack 이 비었을 때만 Queue에서 올린다!!




✏️ 콜백 함수와 프로미스 객체

■ 콜백 함수

콜백 함수

: 다른 함수에 인자로 전달되는 함수.
주로 비동기적 작업이 완료된 후에 호출되거나, 특정 이벤트가 발생했을 때 실행된다.

왜 필요할까?
➜ 비동기 방식은 순서를 보장하지 않기 때문에, 응답의 처리 결과에 의존하는 콜백 함수를 이용해서 순서를 간접적으로 끼워맞출 수 있다.

단점은?
➜ 코드 복잡도를 증가시킨다.


■ 프로미스 객체

Promise 객체

: 비동기 작업이 끝나면 그 결과에 따라 하나의 값을 반환하는 객체

Promise 객체를 사용하면, 콜백 함수를 사용할 때의 코드 복잡도를 개선할 수 있다.

  • Promise 객체는 new Promise(콜백 함수) 로 생성할 수 있다.
  • new Promise(콜백 함수) 에서 콜백 함수는 두 개의 매개변수를 필요로 한다.
    • resolve 매개변수: 비동기 작업이 성공임을 알리는 객체
    • reject 매개변수: 비동기 작업이 실패임을 알리는 객체
const myPromise = new Promise((resolve, reject) => {
	// 비동기 작업 수행
    const data = fetch('URL');
    
    if(data)
    	resolve(data); // 만일 요청이 성공하여 데이터가 있다면
    else
    	reject("Error"); // 만일 요청이 실패하여 데이터가 없다면
})

위의 작업으로 만들어진 myPromise 객체는 비동기 작업이 완료된 이후에 수행될 다음 작업을 연결할 수 있다.
➜ 콜백 함수가 했던 것처럼 순서 보장을 해준다.


⚠️ new Promise 가 생성되면, Promise의 매개변수로 넘겨준 콜백함수는 자동적으로 & Synchronous 하게 실행된다.

하지만, 그 내부에서는 Asynchronous 하기 때문에 비동기적으로 작업이 수행된다.


.then().catch() 메소드 체이닝을 통해 성공과 실패에 대한 후속 처리를 할 수 있다.

myPromise
    .then((value) => { // 성공적으로 수행했을 때 실행될 코드
    	console.log("Data: ", value); // 위에서 return resolve(data)의 data값이 출력된다
    })
    .catch((error) => { // 실패했을 때 실행될 코드
     	console.error(error); // 위에서 return reject("Error")의 "Error"가 출력된다
    })
    .finally(() => { // 성공하든 실패하든 무조건 실행될 코드
    	
    })

.then(), .catch()의 리턴 값은 Promise 객체이다.
.then()의 결과 값에 다시 .then()를 붙일 수 있다!




✏️ async/await

하지만 프로미스도 완벽한 해결책은 아니다.
➜ 지나친 then 핸들러 함수의 남용으로 인한 Promise Hell 발생 가능

async/await

: 비동기 코드를 마치 동기 코드처럼 쉽고 명확하게 작성할 수 있게 만들어 주는 키워드.
Promise를 기반으로 한다.

  • then, catch 메서드를 사용하지 않는다.
  • function 키워드 앞에 async 를 붙이고, 비동기로 처리되는 부분 앞에 await 를 붙여준다.

아래 코드는 async/await을 사용하지 않은 코드이다.

// 기존 Promise.then() 형식
// 프로미스 객체 반환 함수
function delay(ms) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`${ms} 밀리초가 지났습니다.`);
      resolve()
    }, ms);
  });
}

function main() {
  delay(1000)
      .then(() => {
        return delay(2000);
      })
      .then(() => {
        return Promise.resolve('끝');
      })
      .then(result => {
        console.log(result);
      });
}

// 메인 함수 호출
main();

아래의 코드는 async/await을 사용한 코드이다.

// async/await 방식
async function main() {
  await delay(1000);
  await delay(2000);
  const result = await Promise.resolve('끝');
  console.log(result);
}

// 메인 함수 호출
main();



REFERENCE

https://inpa.tistory.com/entry/JS-📚-비동기처리-Promise [Inpa Dev 👨‍💻:티스토리]

profile
백엔드 개발자

0개의 댓글