비동기적 처리를 위한 프로미스와 async/await

이정기·2023년 4월 20일
0

TIL

목록 보기
59/71
post-thumbnail

출처
자바스크립트 비동기 처리와 콜백함수

비동기 처리

비동기처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미

콜백함수

예전 콜백함수를 공부하겠다고 글을 썼는데 비동기 처리를 공부하며 콜백함수를 그냥 지나칠 수 없었다.

특정로직 (예시: Ajax, setTimeout) 의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 실행하는 비동기 처리방식의 문제점 때문에 개발자들은 콜백함수를 도입했다.

// 우수예시
//Ajax 코드의 결과값을 console.log 로 찍었을 때, 비동기 문제로 인한 undefinde 반환
function getData() {
	var tableData;
	$.get('https://domain.com/products/1', function(response) {
		tableData = response;
	});
	return tableData;
}

console.log(getData()); // undefined


// 위에 코드를 콜백함수로 해결
function getData(callbackFunc) {
	$.get('https://domain.com/products/1', function(response) {
		callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
	});
}

getData(function(tableData) {
	console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});

맨 위에 출처에 있는 블로거분이 이해가 잘되는 코드를 예시로 들어줘서 그대로 가져왔다.

콜백함수는 마치 결과값이 나올때까지 기다리라는 예약기능과 같다. 하지만 여러번의 예약을 걸 수도 있는 개발 상황이라면 사람들은 콜백 지옥 이라는 여러번의 중첩 콜백을 써야 하는 때가 온다.

다른 코딩 패턴으로 콜백 족을 해결할 수 있지만, Promise 나 Async 를 이용해 더 편하게 구현 할 수 있다.

프로미스 (Promise)

function getData(callback) {
  // new Promise() 추가
  return new Promise(function(resolve, reject) {
    $.get('url 주소/products/1', function(response) {
      // 데이터를 받으면 resolve() 호출
      resolve(response);
    });
  });
}

// getData()의 실행이 끝나면 호출되는 then()
getData().then(function(tableData) {
  // resolve()의 결과 값이 여기로 전달됨
  console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
});

콜백 예시에 프로미스를 적용한 코드이다.

콜백 함수로 처리하던 구조에서 new Promise(), resolve(), then()와 같은 프로미스 API를 사용한 구조로 바뀌었다.

프로미스를 적둉할 땐, 프로미스를 생성하고 종료될 때까지 프로미스의 3가지 상태를 갖는데 이것에 대해 이해를 하고 있어야 프로미스를 정확히 쓸 수 있다.

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

Pending(대기)

new Promise(function(resolve, reject) {
  // ...
});

호출 시 대기(Pending) 상태가 된다. 호출할 때 콜백 함수를 선언할 수 있고, 인자는 resolve, reject 이다.

Fulfilled(이행)

new Promise(function(resolve, reject) {
  resolve();
});

콜백 함수 인자 resolve() 를 실행시 이행(Fulfilled) 상태가 된다.

function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

이행 상태시 then() 을 이용하여 처리 결과 값을 받을 수 있다.

Rejected(실패)

프로미스 객체 생성 후 인자 reject 를 실행하면 실패 상태가 된다.

new Promise(function(resolve, reject) {
  reject();
});

실패 상태시 실패한 이류를 catch() 로 받을 수 있다.

function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()의 결과 값 Error를 err에 받음
  getData().then().catch(function(err) {
    console.log(err); // Error: Request is failed
});

이행과 실패 적용예제

function getData() {
  return new Promise(function(resolve, reject) {
    $.get('url 주소/products/1', function(response) {
      if (response) {
        resolve(response);
      }
      reject(new Error("Request is failed"));
    });
  });
}

// 위 $.get() 호출 결과에 따라 'response' 또는 'Error' 출력
getData().then(function(data) {
  console.log(data); // response 값 출력
}).catch(function(err) {
  console.error(err); // Error 출력
});

프로미스 여러개 연결(Promise Chaining)

new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})
.then(function(result) {
  console.log(result); // 1
  return result + 10;
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

실무에서 사용방법

데이터 및 콜백함수 정의

var userInfo = {
  id: 'test@abc.com',
  pw: '****'
};

function parseValue() {
  return new Promise({
    // ...
  });
}
function auth() {
  return new Promise({
    // ...
  });
}
function display() {
  return new Promise({
    // ...
  });
}

프로미스로 로직 연결

getData(userInfo)
  .then(parseValue)
  .then(auth)
  .then(diaplay);

프로미스 에러처리 방법

  1. then()의 두번째 인자에 에러 처리
  2. catch()를 이용해 처리

프로미스 에러처리는 가급적 catch()를 사용

이유: 1번째 방법으로 처리시 오류를 제대로 잡아내지 못하고 promise 에러를 띄운다.


async/await

개발자가 읽기 좋은 코드를 작성할 수 있도록 콜백함수와 프로미스의 단점을 보완한 문법

async function 함수명() {
  await 비동기처리 메서드 이름();
}

간단히 실행 순서를 보장 받을 수 있다.

정말 충격적인건 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작한다는 것이다.

무분별하게 써왔떤 async/await 의 반환값을 난 프로미스를 쓰고 있었는데도 몰랐다는 점이다.

예시로, typeORM의 save() 메서드는 Promise 를 반환한다. 데이터베이스 쿼리가 비동기적으로 실행되기 때문이다.

그리고 호출한 이후 다음 작업이 기다릴 필요가 없는 경우 await 키워드를 사용하지 않아도 된다.

이 부분은 좀더 고민 후 코드 작성시 유의해야겠다.
무분별한 async await 사용을 조심하자!

profile
Node.js 로 꿈을 꾸었다..

0개의 댓글