November 14, 2020
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')
})
}
โ๋ด๊ฐ ์ธ์ 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 ์ด๋ผ๋ ์ฝ๋ฐฑํจ์๋ฅผ ์ด์ฉํด์ ์๋ฒ์์ ๊ฐ์ ์ป์ด์๋ค. ์ข์์ข์ ๋ง์์ ๋ ๋ค.
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 ํด ์ฃผ์ด๋ ๋๋๊ตฌ๋!
์ผ์ ์๊ฐ(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 ๋ฅผ ๋ฆฌํดํ๊ฒ ํ๋ ๊ฒ์ด,
๋๊ธฐ์ ์ธ ์ฝ๋๋ฅผ ์ฐ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ํด์ ๊ฐ๋ ์ฑ์ด ๋ ์ข์ ๋ณด์ด๊ธฐ๋ ํ๋ค.
์ด์ ๋ชจ๋ ๋๋ฌด์์ ๊ณผ์ผ์ ์น์ธ์ด ํด๋ณด์.
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์ด) ๐ + ๐ ๋ก ๋ชจ๋ ๋๋ฌด์์ ๊ณผ์ผ์ ๋ฐ ์๋ค. (๋ฐ๋๋๊ฐ ๋๋ฌด์๋๊ฐ?)
ํ์ง๋ง ์ฝ๋ฐฑ ์ง์ฅ์ ์ด์ ํ๋ก๋ฏธ์ค ํฌ ์ง์ฅ์ ์์์ด๋ค. ๋๋ฌด๊ฐ ๋ ์๊ธด๋ค๋ฉด ๋์ฑ ์ฌํ ์ค์ฒฉ์ ์ธ ์ฒด์ด๋์ด ์ด์ด์ง ๊ฒ์ด๋ค.
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์ด ๊ฑธ๋ฆฌ๊ฒ ํ๋๊ฑด ๋๋ฌด ๋นํจ์จ์ด์ง ์๋?
์ฌ๊ณผ ๋ฐ๋๋ฐ 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() ์ ๋ํด ์์๋ด์ผ ํ๋ค.
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 ๋ค์ด ๋ณ๋ ฌ์ ์ผ๋ก ๋ค ๋ฐ์์ฌ ๋๊น์ง ๋ชจ์์ฃผ๋ ์ญํ ์ ๋ด๋นํ๋ค.
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์ด ๋ง์ ๋ฐ์ง๋ ๋ฉ๋ก ์ด ๋จผ์ ์ถ๋ ฅ๋์ด ๋์จ๋ค!