TIL | Backend | Node - 비동기 실습

Wook·2021년 12월 23일
0

TIL | Backend

목록 보기
10/19

일반 함수

function getThree() {
	return 3;
}

console.log(getThree());
// 3

해당 함수는 기본적으로 함수를 선언하는 방법으로 호출시 정상적으로 값을 계산하여 return 하여줍니다.


비동기 함수

async function willGetThree() {
	try {
		return 3;
	}
	catch(err) {
		throw err;
	}
  // throw는 없어도 되나 예시용으로 넣었습니다.
}

console.log(willGetThree());
// Promise {status: "resolved", result: 3}

비동기 함수는 Promise 타입의 객체를 리턴합니다.


프로미스 객체

function getThreePromise() {
 return new Promise((resolve, reject) => {
		try {
			resolve(3); // same in async return!
		}
		catch(err) {
			reject(err); // same in async throw!
		}
    // reject는 없어도 되나 예시용으로 넣었습니다.
	});
}

console.log(getThreePromise());
// Promise {status: "resolved", result: 3}

new Promise()를 통해 Promise 타입의 객체를 생성할 수 있습니다.

Promise 객체는 DB Input/Output, 네트워크 통신 등 요청과 응답에 시간이 걸리는 작업에서 사용됩니다.

이는 지연 시간 동안 다른 작업을 처리하기 쉽게 만들어줍니다.

Promise 객체는 status, result 프로퍼티를 가지고

.then, .catch, .finally 메소드를 가집니다. 각각의 실행 방법은 아래에서 설명하겠습니다.

async 함수를 호출 한다는 것 = 프로미스 객체를 반환하는 것입니다.

즉,

async 함수의 return이 Promise 객체의 resolve가 되고

async 함수의 throw가 Promise 객체의 reject가 됩니다!

resolve는 Promise 객체의 then으로 리턴값을 가져올 수 있으며

reject는 Promise 객체의 catch로 에러값을 가져올 수 있습니다.

아래 예시 코드를 보겠습니다.

Promise 객체 사용법

const getThreePromise = () => new Promise((resolve, reject) => {
	resolve(3);
});

// bad
console.log(getThreePromise().result); // undefined

// good
getThreePromise.then((result) => console.log('result :', result));

// result : 3

Promise 객체의 result로 바로 접근하면 undefined를 리턴받습니다.

그래서 .then을 이용하여 실제 값에 접근해야 합니다.

마찬가지로, .catch를 이용하면 에러 발생시, 에러 값(에러 객체)에 접근할 수 있습니다.


async/await 사용법

async function willGetThree() {
	return 3;
}

async function main() {
	const three1 = await willGetThree();
	
	console.log(three1);
	// 3
}

이렇게 async 함수내에서 await를 사용할 수 있습니다. 이렇게 하면 해당 값은 실제 값을 내보내게 됩니다.


Promise의 then, catch를 사용하다보면 콜백지옥에 빠진다.

const getCoffeeList = new Promise((resolve) => {
  setTimeout(() => {
    let name = "Espresso";
    console.log(name);
    resolve(name);
  }, 500);
}).then((prevName) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      let name = prevName + ", Americano";
      console.log(name);
      resolve(name);
    }, 500);
  }).then((prevName) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        let name = prevName + ", Mocha";
        console.log(name);
        resolve(name);
      }, 500);
    }).then((prevName) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          let name = prevName + ", Latte";
          console.log(name);
          resolve(name);
        }, 500);
      });
    });
  });
});
// Espresso, Americano, Mocha, Latte

Promise로 로직을 구성할 경우 해당 코드와 같이 콜백안에 콜백을 쓰는 형태가 될 수 있습니다. (콜백 지옥..)

해결법

const getCoffeeList = (prevCoffeeList, newCoffee) => {
	return new Promise((resolve) => {
    setTimeout(() => {
			resolve(prevCoffeeList + ', ' + newCoffee);
		}, 1000);
  });
}

async function main() {
	let coffeeList = await getCoffeeList('', 'Espresso'); // 초기값은 빈 string
	coffeeList = await getCoffeeList(coffeeList, 'Americano');
	coffeeList = await getCoffeeList(coffeeList, 'Mocha');
	coffeeList = await getCoffeeList(coffeeList, 'Latte');

  console.log(coffeeList);
  // Espresso, Americano, Mocha, Latte
}

main();

async / await를 이용하여 함수를 만들면 콜백지옥에 빠지지 않습니다.



한번에 여러가지 일을 하고 결과들을 받아야할 때

async function getName(id) { ...DB에서 가져와서 이름을 return 하는 함수 };

async function getAge(id) { ...DB에서 가져와서 나이를 return 하는 함수 }

async function getGender(id) { ...DB에서 가져와서 성별을 return 하는 함수 }

async function getInfoSlow(id) {
	const name = await getName(id);
  const age = await getAge(id);
  const gender = await getGender(id);

  return {
    name,
    age,
    gender
  }
}
// It's okay but slow

await 를 쓰게 되면 다음과 같이 여러가지 일을 한번에 진행할 수 있음에도 불구하고 차례대로 결과를 받아오게 코드를 작성하게 됩니다.

  • 타임 라인

해결책

async function getName(id) { ...DB에서 가져와서 이름을 return 하는 함수 };

async function getAge(id) { ...DB에서 가져와서 나이를 return 하는 함수 }

async function getGender(id) { ...DB에서 가져와서 성별을 return 하는 함수 }

async function getInfoFast(id) {
	const results = await Promise.all([ getName(id), getAge(id), getGender(id) ]);
	// .then, .catch도 사용할 수 있습니다.

	return {
    name: results[0],
    age: results[1],
    gender: results[2]
  }
}
// It's fast

Promise.all 메소드를 사용해서 Array 안 Promise들이 모두 resolve 되었을 때, 실제 값들을 넘길 수 있습니다.

  • 타임라인


결론

  1. async 함수를 쓰면 Promise 타입을 반환하게 된다. ( Promise<return 값 타입> )
  2. .then을 쓰면 return 값을 들고 올 수 있다. 다만 콜백을 써서 들고와야한다.
  3. await을 쓰면 return 값을 들고 올 수 있다. 다만 async 함수 내에서 사용해야한다.
  4. 콜백 지옥을 해결하기 위해 async, await 문법에 친숙해져야 합니다.
  5. 비동기 함수를 동시에 호출해서 결과를 받고 싶다면 Promise.all 메소드를 사용합니다.
profile
지속적으로 성장하고 발전하는 진취적인 태도를 가진 개발자의 삶을 추구합니다.

0개의 댓글