callback, promise, async/await

Donghun Seol·2022년 9월 16일
0

node.js 교과서

목록 보기
3/12

node.js 교과서 2장 보조 포스팅

callback, promise, async/await 패턴으로 변화하는 과정 실습하면서 포스팅.

callback

세 개의 콜백함수로 이루어진 형태.
만약 콜백함수가 5개, 8개가 된다면? 생각만해도 골치아픔..
에러 처리도 각각 해줘야 되어 더욱 복잡해진다.

function findAndSaveUser(Users) {
  User.findOne({}, (err, user) => {
    if (err) {
      return console.log(err);
    }
    user.name = 'seol';
    user.save((err) => {
      if (err) {
        return console.log(err);
      }
      User.findOne({gender: 'm'}, (err, user) => {
        console.log(user);
      });
	});
  });
}

promise

promise 객체는 일단 호출해놓고, 필요할 때 결괏값을 .then으로 꺼내 쓸 수 있음.
에러처리를 .catch에서 한번에 할 수 있음.
아래의 예제는 findOne과 save메서드가 Promise객체를 반환한다고 가정했을때 가능한 리팩터링이다.

function findAndSaveUser(Users) {
  Users.findOne({})
    .then((user) => { // user는 Users.findOne()에서 반환된 Promise 객체의 resolve()에 담긴 매개변수
      user.name='seol';
      return user.save();
    })
    .then((user) => { // user는 user.save()에서 반환된 Promise
      return Users.findOne({ gender: 'm' });
    })
    .then((user) => {
      console.log(user)
    })
    .catch(err => {
      console.error(err);
    });
}

async/await

async/await is syntactic sugar for promise chain
코드가 많이 짧아졌지만 try, catch문 오류처리는 꼭 해줘야 한다.
async로 선언된 함수의 반환값은 항상 promise가 된다.
스트링을 리턴해주더라도 결국 resolve('return value'); 식으로 promise 객체를 리턴해 준것과 같다.

async function findAndSaveUser(Users) {
  try {
    let user = await Users.findOne({});
    user = await user.save();
    user = await Users.findOne({ gender: 'm' });
    console.log(user);
  } catch (err){
    console.log(err);
  }
}

아래의 두 함수의 리턴 값은 동일.

function fetchUser() {
  return new Promise((resolve, reject) => {
    // do network req for 10 secs...
    resolve('your username is seol');
  });
}

async function fetchUser2() {
  return 'your username is seol';
}

아래는 드림코딩 유튭에서 학습한 코드
.then 체이닝을 async await 활용하여 간결하게 표현 가능하고,
promise.all([array of promises])로 동시적 실행도 가능하다.
promise.all은 꼭 숙지해야함, promise.race도 있다. 무슨 뜻인지는 알제?

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
} // reusable delay function

async function getBanana() {
  await delay(3000);
  return 'banana'
}

async function getApple() {
  await delay(3000);
  return 'apple'
}

function pickFruits() {
  return getApple().then(apple => {
    return getBanana().then(banana => {
      return apple + ' ' + banana;
    });
  });
}

pickFruits().then(console.log) // .then에 인자 없이 함수만 이름만 넘기면 기본인자를 소비하는 형태로 함수를 호출한다. 아래와 동일한 함수임
pickFruites().then(data => { console.log(data) });

// refactor pickFruits with async/await
async function pickFruitsConcise() {
  const apple = await getApple();
  const banana = await getBanana();
  return apple + ' ' + banana
} // still it takes 3 + 3 seconds to run

// if you want to run getApple(), getBanana() concurrently use below
async function pickFruitsConcurrent1() {
  const applePromise = getApple();
  const bananaPromise = getBanana();

  // wait for above promises here
  const apple = await applePromise;
  const banana = await bananaPromise;
  
  return apple + ' ' + banana
}

// Promise.all makes above code much more prettier!!!
async function pickFruitsConcurrent2() {
  return Promise.all([getApple(), getBanana()])
    .then(fruits => fruits.join(' '));
}

pickFruitsConcurrent2.then(console.log);

promise with map

juiceMap의 값은은 getFruits에서 반환한 promise가 resolved된 결과가 emoji에 변수에 저장되는 것 처럼 보이지만 그렇지 않고 pending한 promise가 바로 저장된다.
juiceFor처럼 활용해야 의도한 값이 나온다.
만약 전체를 병렬적으로 수행하려면 juiceParallel 처럼 await의 위치를 for 옆에 넣으면 된다.

const fruits = {
    pineapple: "🍍",
    peach: "🍑",
    banana: "🍌",
};
const fruitsArr = ['peach', 'pineapple', 'banana']

async function getFruits(name) {
    console.log('getFruits', name, fruits[name]);
    return fruits[name];
}


const juiceMap = fruitsArr.map(async f => {
    const emoji = await getFruits(f);
    console.log(emoji);
    return emoji;
});
console.log(juiceMap); // [ Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]

const juiceFor = async () => {
    for (const f of fruitsArr) {
        const emoji = await getFruits(f);
        console.log(f, emoji);
    }
};

juice2();

const juiceForParallel = async () => {
    for await (const f of fruitsArr) {
        const emoji = getFruits(f);
        console.log(f, emoji);
    }
};
profile
I'm going from failure to failure without losing enthusiasm

0개의 댓글