[Javascript] callback, Promise, async - await

SNXWXH·2024년 6월 2일

Javascript

목록 보기
3/13
post-thumbnail

동기 / 비동기 통신의 이해

  • 동기(Synchronous)
    • 한번에 하나의 함수만 실행 → 하나의 함수가 실행되는 동안 나머지는 blocking
    • 싱글스레드, 싱글 프로세스
  • 비동기(Asynchronous)
    • 한번에 여러 개의 함수 실행 → I/O를 수행하는 비동기 함수는 background에 넘김(Non-blocking)
    • 싱글스레드, 멀티 프로세스
    • 스레드가 실행되고 있을 때 다른 작업을 실행할 수 있는 것

Callback

콜백은 비동기 완료 시 실행할 함수를 전달하는 패턴으로 주로 다른 함수가 비동기 작업을 수행한 후 호출된다.

콜백은 일반적으로 함수의 매개변수로 전달된다.

callback 단점

  • 함수의 매개 변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기가 계속되어 코드 가독성 악화 ⇒ 콜백지옥(callback hell)
  • 복잡한 비동기 작업을 다루기 어려움
step1(function (value1) {
  step2(function (value2) {
    step3(function (value3) {
      step4(function (value4) {
        step5(function (value5) {
          step6(function (value6) {
            // Do something with value6
          });
        });
      });
    });
  });
});

이러한 콜백 지옥을 해결하기 위해 ES6에서 Promise를 도입한다.

Promise

Promise는 ES6에서 도입된 객체로 비동기 작업을 더 구조화하고 관리하기 위한 방법을 제공한다.

대기(pending), 이행(fulfilled), 거부(rejected) 상태를 가질 수 있으며, 비동기 성공/실패 시 처리를 위한 then(), catch() 메서드를 제공한다.

Promise는 체이닝(Chaning)을 통해 여러 비동기 작업을 연결하여 가독성 좋은 코드를 작성할 수 있게 한다.

const promise = new Promise((resolve, reject) => {
	// 처리 내용
})

promise.then( (data) => {
	return new Promise((resolve, reject) => {
    	// 처리 내용
    }). then((data2) => {
    	// 처리 내용
    }).catch(
    	console.log(ErrorMessage)
    )
})
.catch(
	console.log(ErrorMessage)
)

대기(pending) 상태

Promise 객체가 생성되고, 비동기 작업이 아직 완료되지 않은 초기의 상태를 뜻한다.

해당 상태에서는 resolve 또는 reject 함수가 호출되지 않은 상태를 뜻한다.

const myPromise = new Promise(function(resolve, reject) {
  // 비동기 작업 수행
  // 아직 작업이 완료되지 않음
});

이행(fulfilled) 상태

비동기 작업이 성공적으로 완료되어 Promise 객체가 결과값을 반환하는 상태를 뜻한다.

이때 resolve 함수가 호출되며, 결과값이 전달된다.

const myPromise = new Promise(function(resolve, reject) {
  // 비동기 작업 성공 시
  resolve('성공적으로 완료된 결과');
});

거부(rejected) 상태

비동기 작업이 실패하거나, 오류가 발생한 경우인 상태를 뜻한다.

이때 reject 함수가 호출되며, 에러 정보가 전달된다.

const myPromise = new Promise(function(resolve, reject) {
  // 비동기 작업 실패 시
  reject('에러 발생');
});

Promise.all()

Promise.all()은 여러개의 Promise들을 비동기적으로 실행하여 처리할 수 있다.

배열형태의 Promise들을 인자로 받고, 이 Promise들은 병렬로 실행되며, 모든 Promise들이 해결될 때까지 기다린다.

그 후 모든 Promise가 성공적으로 해결되었을때, Promise.all()은 해당 Promise들의 결과를 배열로 반환한다.

하지만 여러개의 Promise들 중 하나라도 reject를 반환하거나 에러가 날 경우, 모든 Promise들을 reject 시킨다. 만약 하나라도 Promise가 거부되면, 첫 번째 거부된 Promise를 반환하고, 나머지 Promise들은 무시된다.

그럼에도 Promise.all()을 사용하는 이유는 병렬로 실행되는 Promise들을 효율적으로 관리하고,
모든 Promise의 결과를 한 번에 처리할 수 있다는 점이다.

이는 비동기 작업을 동시에 처리하고, 작업이 완료되었을 때 결과를 수집해야 하는 상황에서 유용하다.

// 1) async/await
async func1(){
	const community_with_likes = await community.map(async (item) => {
      const like_list = await this.communityLikesRepository.find({
        where: {
          community_id: item.community_id,
        },
      });
      return like_list;
    });
}

// 2) Promise.all()
async func2(){
	const community_with_likes = await Promise.all(
      community.map(async (item) => {
        const like_list = await this.communityLikesRepository.find({
          where: {
            community_id: item.community_id,
          },
        });
        return like_list;
      }),
    );
}

async - await

ES2017에 도입된 비동기 패턴으로 Promise의 체이닝을 더 간결하게 정리하여 사용할 수 있다.

async 함수는 비동기 작업을 수행하고, await 키워드를 사용하여 Promise의 완료를 기다린다. 이처럼 코드는 동기처럼 보이면서 실제로는 비동기 작업을 수행한다.

try-catch 블록을 사용하여 에러 처리가 가능하고, 코드의 가독성을 높이며 콜백 지옥도 피할 수 있다.

또한 async 함수를 실행하게 되면 무조건 Promise 객체가 반환되고, return은 반환된 Promise 객체의 결과값이다.

function Data() {
	return new Promise((resolve, reject) => {
    	setTimeout(() => {
        	return resolve('성공')
        }, 1000)
    })
}

async function loadData(){
	try{
    	const result = await Data()
        console.log(result)
    } catch(e){
    	console.log(e)
    }
}

loadData()

📍참고

profile
세상은 호락호락하지 않다. 괜찮다. 나도 호락호락하지 않으니까.

0개의 댓글