πŸ€™πŸ»Promise Part(5) async, await, Promise.all(), Promise.race()

πŸ€™πŸ»Syntactic Sugar

async, await 은 Promise λ₯Ό μ’€ 더 κ°„κ²°ν•˜λ©΄μ„œ κ°„νŽΈν•˜κ²Œ 그리고 λ™κΈ°μ μœΌλ‘œ μ‹€ν–‰λ˜λŠ” 것 처럼 보이게 λ§Œλ“œλŠ” Syntatic Sugar 이닀.

기쑴에 μ‘΄μž¬ν–ˆλ˜ Promise λ₯Ό κ°μ‹Έμ„œ 더 κ°„νŽΈν•˜κ²Œ,

ν”„λ‘œλ―ΈμŠ€μ™€ .then 으둜 μ΄μ–΄μ§€λŠ” λ˜λ‹€λ₯Έ Promise Hell 을 ν•΄κ²°ν•΄ 쀄 수 μžˆλŠ” 방식이기도 ν•˜λ‹€.

ν•˜μ§€λ§Œ 무쑰건 async, await 으둜만 promise λ₯Ό λŒ€μ²΄ν•΄μ„œ μ‚¬μš©ν•΄λΌ λΌλŠ” μ˜λ―ΈλŠ” μ•„λ‹ˆλ‹€.

μž˜μ€ λͺ¨λ₯΄μ§€λ§Œ 상황에 따라 promise λ₯Ό μ“°κ±°λ‚˜ ν˜Ήμ€ async, await 을 μ“°λŠ” 것이라 ν•œλ‹€.

πŸ˜˜λ‹€μ‹œ, 이걸 μ™œ μ“°λŠ”λ°?

μ‚¬μš©μž 데이터λ₯Ό λ°±μ—”λ“œ (μ„œλ²„) μ—μ„œ λ°›μ•„μ˜€λŠ” 역할을 ν•˜λŠ” fetchUser ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄ 보자.

그리고 κ·Έ μ½”λ“œλŠ” 역할을 μˆ˜ν–‰ν•˜λŠ”λ° λŒ€λž΅ 10초 정도 κ±Έλ¦°λ‹€κ³  κ°€μ •ν•΄ 보자.

function fetchUser() {
  // do network request in 10secs..
  return 'GangGunma'
}

const user = fetchUser()
console.log(user)

였였래.. κ±Έλ¦¬λŠ” μ½”λ“œλΌ κ°€μ •ν–ˆλ‹€.

이렇듯 였래 κ±Έλ¦¬λŠ” μ½”λ“œμ— λŒ€ν•΄ 비동기 적으둜 λŒμ•„κ°€κ²Œλ” ν•΄μ£ΌλŠ” 처리λ₯Ό μ „ν˜€ ν•˜μ§€ μ•Šμ•˜μ„ λ•Œ, Javascript Engine 은 λ™κΈ°μ μœΌλ‘œ μ½”λ“œλ₯Ό μˆ˜ν–‰ν•˜κΈ° λ•Œλ¬Έμ—,

ν•œ 쀄 ν•œ 쀄이 λλ‚˜μ•Ό λ‹€μŒ μ€„λ‘œ λ„˜μ–΄κ°€λŠ” 동기적인 μ½”λ“œλ₯Ό μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ—,

fetchUser() λ‚΄μ—μ„œ 10μ΄ˆκ°„ 머무λ₯΄κ³  μžˆλ‹€κ°€ κ·Έμ œμ„œμ•Ό β€˜GangGunma’ κ°€ 리턴이 λœλ‹€.

κ·Έμ œμ„œμ•Ό λ¦¬ν„΄λœ 값을 λ°›μ•„ 화면에 찍히게 ν•œλ‹€.

λ§Œμ•½ μœ„μ˜ μ½”λ“œ λ‹€μŒμ— Web ν™”λ©΄μ˜ UI λ₯Ό μ²˜λ¦¬ν•˜λŠ” μ½”λ“œκ°€ μžˆμ—ˆλ‹€λ©΄ μ›Ή νŽ˜μ΄μ§€μ— μ ‘μ†ν•œ μ‚¬μš©μžλŠ” 10초 λ™μ•ˆ ν……ν…… λΉ„μ–΄μžˆλŠ” 화면을 λ΄μ•Όλ§Œ ν•  것이닀.

λ°”λ‘œ μ΄λŸ¬ν•œ 였래 κ±Έλ¦¬λŠ” μ½”λ“œ, 일듀을 비동기 적으둜 μ²˜λ¦¬ν•΄ μ£Όμ–΄μ•Ό ν•œλ‹€!

μ•„λž˜μ™€ 같이 말이닀! (λˆˆκ°κ³ λ„μ“Έμˆ˜μžˆλ‹€)

function fetchUser() {
  return new Promise((resolve, reject) => {
    resolve('GangGunma')
  })
}

πŸ‘¨πŸ»β€πŸ’»λ‹€μ‹œ, Promise 의 μ •μ˜

β€œλ‚΄κ°€ μ–Έμ œ user 의 데이터λ₯Ό λ°›μ•„μ˜¬μ§€ λͺ¨λ₯΄κ² μ§€λ§Œ, 이 Promise λΌλŠ” Object λ₯Ό κ°€μ§€κ³  있으면 μ—¬κΈ° λ„€κ°€ then μ΄λΌλŠ” μ½œλ°±ν•¨μˆ˜λ§Œ 등둝해 λ†“μœΌλ©΄

user 의 data κ°€ μ€€λΉ„λ˜λŠ” λŒ€λ‘œ λ„€κ°€ λ“±λ‘ν•œ μ½œλ°±ν•¨μˆ˜λ₯Ό λ‚΄κ°€ λΆˆλŸ¬μ£Όκ² λ‹€κ³  μ•½μ†ν• κ²Œ!”

function fetchUser() {
  return new Promise((resolve, reject) => {
    return 'GangGunma';
  })
}

fetchUser()
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined

new Promise 객체λ₯Ό λ¦¬ν„΄ν•˜μ§€λ§Œ resolve λ‚˜ reject 둜 ν˜ΈμΆœν•˜μ§€ μ•Šμ•˜κΈ°μ—

κ°œλ°œμžλ„κ΅¬μ—μ„œ 보듯이 state κ°€ 계속 pending μƒνƒœμ΄λ‹€.

κ·Έλž˜μ„œ κΌ­ Promise μ•ˆμ—λŠ” resolve λ‚˜ reject λ₯Ό μ΄μš©ν•΄ μ™„λ£Œλ₯Ό μ§€μ–΄μ€˜μ•Ό ν•œλ‹€.

function fetchUser() {
  return new Promise((resolve, reject) => {
    resolve('GangGunma');
  })
}

fetchUser()
Promise {<fulfilled>: "GangGunma"}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "GangGunma"

이제 Promise μƒνƒœκ°€ κ°œλ°œμžλ„κ΅¬μ—μ„œ fulfilled 둜 λ°”λ€Œκ²Œ λ˜λ©΄μ„œ PromiseResult κ°€ GangGunma 둜 바뀐 κ±Έ 확인할 μˆ˜κ°€ μžˆλ‹€.

κ·Έλ ‡κ²Œ ν•΄μ„œ λ§Œλ“€μ–΄λ†“μ€ Producer λ₯Ό μ‚¬μš©ν•˜λ„λ‘ consumer λ₯Ό μ„€μ •ν•΄ 보면,

const user = fetchUser();
user.then(console.log)
GangGunma
Promise {<fulfilled>: undefined}

.then μ΄λΌλŠ” μ½œλ°±ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄μ„œ μ„œλ²„μ—μ„œ 값을 μ–»μ–΄μ™”λ‹€. μ’‹μ•„μ’‹μ•„ λ§ˆμŒμ— λ“ λ‹€.

🌈async λ°”λ₯΄κΈ°

function fetchUser() {
  return new Promise((resolve, reject) => {
    resolve('GangGunma')
  })
}

const user = fetchUser()
user.then(console.log)

μœ„μ™€ 같이 Promise μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ§€ μ•Šκ³ λ„ κ°„νŽΈν•˜κ²Œ 비동기 μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 방법이 μžˆλ‹€.

이 λΈ”λ‘œκ·Έμ˜ 주제 쀑 첫번째인 async λ₯Ό μ†Œκ°œν•˜λŠ” μ‹œκ°„μ΄λ‹€.

async function fetchUser() {
    return 'GangGunma';
}

fetchUser()
Promise {<fulfilled>: "GangGunma"}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "GangGunma"

function μ•žμ— async λΌλŠ” ν‚€μ›Œλ“œλ§Œμ„ μ“°λŠ” κ²ƒμœΌλ‘œ μ•žμ„œ λ§Œλ“€μ—ˆλ˜ Promise Producer 와 λ˜‘κ°™μ€ κΈ°λŠ₯을 μˆ˜ν–‰ν•œλ‹€.

λ˜‘κ°™μ΄ fetchUser κ°€ Promise λ₯Ό λ¦¬ν„΄ν•œλ‹€.

async λΌλŠ” ν‚€μ›Œλ“œλ§ŒμœΌλ‘œ μ½”λ“œλΈ”λŸ­μ΄ μžλ™μœΌλ‘œ Promise 둜 λ°”λ€Œκ²Œ λ˜λŠ”κ΅¬λ‚˜!

κ·Έ κ²°κ³Ό resolve, reject, new Promise ν‚€μ›Œλ“œλ₯Ό μ“°μ§€ μ•Šκ³  λ™κΈ°μ μœΌλ‘œ μ½”λ“œλ₯Ό μ“°λ“― λ°”λ‘œ return ν•΄ 주어도 λ˜λŠ”κ΅¬λ‚˜!

🍎await λ₯Ό 톡해 μ•Œμ•„λ³΄λŠ” κ³ΌμΌμ–»κΈ°πŸŒ

일정 μ‹œκ°„(ms) 이 μ§€λ‚˜λ©΄ resolve λ₯Ό ν˜ΈμΆœν•˜κ²Œ ν•˜λŠ” μ§€μ—° ν•¨μˆ˜μ΄λ‹€.

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

그리고 각각 사과와 λ°”λ‚˜λ‚˜λ₯Ό λ¦¬ν„΄ν•˜λŠ” Promise λ₯Ό λ§Œλ“€μ—ˆλ‹€.

μœ„μ˜ μ§€μ—° ν•¨μˆ˜ delay(ms) λ₯Ό 톡해 각각의 과일을 λ”°λŠ” μ‹œκ°„μ„ λ”œλ ˆμ΄ μ‹œμΌœ 쀄건데,

λ°”λ‘œ μ—¬κΈ°μ„œ await 을 μ‚¬μš©ν–ˆλ‹€.

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

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

await λŠ” delay(3000) 이 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ € μ€€λ‹€.

즉, 3초 μžˆλ‹€κ°€ β€˜μ‚¬κ³Όβ€™ ν˜Ήμ€ β€˜λ°”λ‚˜λ‚˜β€™ λ₯Ό λ¦¬ν„΄ν•˜λŠ” Promise λ₯Ό λ§Œλ“€μ—ˆλ‹€.

사싀 μœ„μ˜ μ½”λ“œκ°€ async, await λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄ μ•„λž˜ μ½”λ“œμ²˜λŸΌ μ“Έ 수 μžˆλ‹€.

function getBanana() {
  return delay(3000).then(() => '🍌')
}

μœ„μ˜ μ½”λ“œ λ³΄λ‹€λŠ” μ–΄μ©Œλ©΄ async, await ν‚€μ›Œλ“œλ₯Ό μ¨μ„œ delay κ°€ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ Έλ‹€κ°€ banana λ₯Ό λ¦¬ν„΄ν•˜κ²Œ ν•˜λŠ” 것이,

동기적인 μ½”λ“œλ₯Ό μ“°λŠ” κ²ƒμ²˜λŸΌ 보이게 ν•΄μ„œ 가독성이 더 μ’‹μ•„ 보이기도 ν•œλ‹€.

이제 λͺ¨λ“  λ‚˜λ¬΄μ—μ„œ 과일을 싹쓸이 ν•΄λ³΄μž.

🍎pickFruits() 둜 과일 λ‹€ λ”°μ˜€κΈ°πŸŒ(1) - Promise Hell Begins

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

pickFruits().then(console.log)
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
🍎 + 🍌

μ •ν™•νžˆ 6초 뒀에 (사과 3초, λ°”λ‚˜λ‚˜ 3초) 🍎 + 🍌 둜 λͺ¨λ“  λ‚˜λ¬΄μ—μ„œ 과일을 λ”° μ™”λ‹€. (λ°”λ‚˜λ‚˜κ°€ λ‚˜λ¬΄μ˜€λ˜κ°€?)

ν•˜μ§€λ§Œ 콜백 μ§€μ˜₯에 이은 ν”„λ‘œλ―ΈμŠ€ ν—¬ μ§€μ˜₯의 μ‹œμž‘μ΄λ‹€. λ‚˜λ¬΄κ°€ 더 생긴닀면 λ”μš± μ‹¬ν•œ 쀑첩적인 체이닝이 μ΄μ–΄μ§ˆ 것이닀.

🍎pickFruits() 둜 과일 λ‹€ λ”°μ˜€κΈ°πŸŒ(2) - async, await ν‚€μ›Œλ“œ 써보기!

async function pickFruits() {
  const apple = await getApple();
  const banana = await getBanana();
  return `async 와 await 을 μ‚¬μš©ν•œ 과일 λ”°κΈ° -> ${apple} + ${banana}`;
}

pickFruits().then(console.log)
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined

async 와 await 을 μ‚¬μš©ν•œ 과일 λ”°κΈ° -> 🍎 + 🍌

λ§ˆμ°¬κ°€μ§€λ‘œ 6초 뒀에 사과와 λ°”λ‚˜λ‚˜λ₯Ό λ”°μ™”λ‹€.

async, await ν‚€μ›Œλ“œλ₯Ό 톡해 λ™κΈ°μ μœΌλ‘œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ“―μ΄ μžμ—°μŠ€λŸ½κ²Œ μž‘μ„±ν•˜κ³ , 리턴값도 μžμ—°μŠ€λŸ½κ²Œ μž‘μ„±ν•˜λ‹ˆ λ„ˆλ¬΄ κ°„νŽΈν•΄ 보인닀.

λ§Œμ•½ async function getApple() 의 λ‚΄λΆ€ μ½”λ“œμ— throw β€˜error’ κ°€ μžˆλ‹€λ©΄,

async function pickFruits() {
  try {
    const apple = await getApple();
    const banana = await getBanana();
  } catch() {
    // μ—λŸ¬ 처리 μ½”λ“œ μž‘μ„±.
  }
  return `async 와 await 을 μ‚¬μš©ν•œ 과일 λ”°κΈ° -> ${apple} + ${banana}`;
}

try, catch λ₯Ό 톡해 μž‘μ„±ν•  μˆ˜κ°€ μžˆλ‹€.

μ•„λ‹ˆ 근데 사과 λ”°λŠ”κ±° 3초 기닀리고 λ°”λ‚˜λ‚˜ λ”°λŠ”κ±° 3초 기닀리고 κΌ­ μ΄λ ‡κ²Œ 해야돼?

μ‚¬κ³Όλž‘ λ°”λ‚˜λ‚˜ λ”°λŠ” 게 λ¨Ό 상관이라고… 6초 걸리게 ν•˜λŠ”κ±΄ λ„ˆλ¬΄ λΉ„νš¨μœ¨μ΄μ§€ μ•Šλ‚˜?

🍎🍌 await λ³‘λ ¬μ²˜λ¦¬

사과 λ”°λŠ”λ° 3초, λ°”λ‚˜λ‚˜λ₯Ό λ”°λŠ”λ° 3초..

μ΄λŸ¬ν•œ 순차 진행은 λΉ„νš¨μœ¨ κ°™λ‹€. μ™œλƒλ©΄ λ°”λ‚˜λ‚˜, μ‚¬κ³ΌλŠ” 각자 λ”°λŠ” ν–‰μœ„μ—μ„œλŠ” μ„œλ‘œ 연관이 없기에

사과 λ‹€λ”Έλ•ŒκΉŒμ§€ λ°”λ‚˜λ‚˜λ₯Ό λͺ»λ”°κ³  기닀릴 ν•„μš”κ°€ μ „ν˜€ μ—†λŠ”κ±°λ‹€.

λ§Œλ“€μž 마자 λ°”λ‘œ μ‹€ν–‰λ˜μ–΄ λ²„λ¦¬λŠ” ν”„λ‘œλ―ΈμŠ€μ˜ νŠΉμ„±μ„ ν™œμš©ν•΄ 보자!

async function pickFruits() {
  const applePromise = getApple();
  const bananaPromise = getBanana();
  // 사과와 λ°”λ‚˜λ‚˜μ˜ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ§Œλ“€μ—ˆλ‹€. λ§Œλ“€λ©΄ λ°”λ‘œ μ‹€ν–‰λ˜λŠ” promise 의 νŠΉμ„±μ„ 이용!
  const apple = await applePromise; // 동기화 μˆ˜ν–‰
  const banana = await bananaPromise; // 동기화 μˆ˜ν–‰
  return `병렬적 μ‹€ν–‰ 이제 3μ΄ˆλ°–μ— μ•ˆκ±Έλ¦°λ‹€! ${apple} + ${banana}`;
}

pickFruits().then(console.log)
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined

병렬적 μ‹€ν–‰ 이제 3μ΄ˆλ°–μ— μ•ˆκ±Έλ¦°λ‹€! 🍎 + 🍌

사과와 λ°”λ‚˜λ‚˜μ˜ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ§Œλ“€κ³ , λ§Œλ“€λ©΄ λ°”λ‘œ μ‹€ν–‰λ˜λŠ” ν”„λ‘œλ―ΈμŠ€μ˜ νŠΉμ„±μ„ μ΄μš©ν•΄μ„œ 이제 λ³‘λ ¬μ μœΌλ‘œ μ½”λ“œκ°€ μ‹€ν–‰λ˜κΈ°μ—,

λ§Œλ“€μž 마자 λ™μ‹œμ— λ”°μ„œ κΈ°λ‹€λ € 놨닀가 μ¨λ¨ΉλŠ” 것이닀.

사과 3초 그리고 λ°”λ‚˜λ‚˜ 3초.. 6μ΄ˆκ°€ κ±Έλ¦¬λŠ” 것이 μ•„λ‹ˆλΌ,

λͺ¨λ‘ λ”°λŠ” 데 3초 κ±Έλ¦¬λŠ” 것이닀.

3초 λ§Œμ— λ³‘λ ¬μ μœΌλ‘œ μ‹€ν–‰, 즉 λ™μ‹œ λ‹€λ°œμ μœΌλ‘œ κΈ°λŠ₯을 μˆ˜ν–‰ν•˜κ²Œ λ§Œλ“œλŠ” μ…ˆμ΄λ‹€.

ν•˜μ§€λ§Œ 맀우 λ”ν‹°ν•œ μ½”λ“œμ΄λ‹€. Promise.all() 에 λŒ€ν•΄ μ•Œμ•„λ΄μ•Ό ν•œλ‹€.

πŸ‘½λ°°μ—΄μ„ μ „λ‹¬ν•˜λŠ” Promise.all()

function pickAllFruits() {
  return Promise.all([getApple(), getBanana()])
  .then(fruits => fruits.join(' + '))
}

pickAllFruits().then(console.log)
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined

🍎 + 🍌

Promise.all() 은 λ‚΄λΆ€ 인자둜 ν”„λ‘œλ―ΈμŠ€ 배열을 μ „λ‹¬ν•˜κ²Œ λ˜λŠ”λ° 이 λ•Œ, λͺ¨λ“  Promise 듀이 λ³‘λ ¬μ μœΌλ‘œ λ‹€ λ°›μ•„μ˜¬ λ•ŒκΉŒμ§€ λͺ¨μ•„μ£ΌλŠ” 역할을 λ‹΄λ‹Ήν•œλ‹€.

πŸŒ±λ¨Όμ € λ”°μ˜¨ 첫번째 과일만 λ°›μ•„μ˜€λŠ” Promise.race()

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

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

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

async function getMelon() {
  await delay(1000)
  return '🍈';
}

function pickOnlyOne() {
  return Promise.race([getApple(), getBanana(), getMelon()])
}

pickOnlyOne().then(console.log)

pickOnlyOne().then(console.log)
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined

🍈

Promise.race() λŠ” 인자둜 배열에 μ „λ‹¬λœ Promise μ€‘μ—μ„œ κ°€μž₯ λ¨Όμ € 값을 λ¦¬ν„΄ν•˜λŠ” Promise 만 전달이 λ˜μ–΄μ§„λ‹€.

1초 λ§Œμ— λ”°μ§€λŠ” 멜둠이 λ¨Όμ € 좜λ ₯λ˜μ–΄ λ‚˜μ˜¨λ‹€!


[DotoriMook]
Written by@[DotoriMook]
ν”„λ‘ νŠΈμ—”λ“œ μ£Όλ‹ˆμ–΄ 개발자 λ„ν† λ¦¬λ¬΅μ˜ 기술개발 λΈ”λ‘œκ·Έ μž…λ‹ˆλ‹€.

GitHubMediumTwitterFacebook