3. Async/await Internals

promise๋Š” async/await์™€ ํ†ตํ•ฉ์„ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ˆ˜๋‹จ์ž…๋‹ˆ๋‹ค. promise๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์›์ ๋ถ€ํ„ฐ ์•Œ์•„๋ณด์•˜์œผ๋‹ˆ ์ด์ œ ์‹œ์•ผ๋ฅผ ๋„“ํ˜€์„œ promise์— await์„ ์ ์šฉํ•  ๋•Œ ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜๋Š”์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค. ๋น„๋ก async ํ•จ์ˆ˜๊ฐ€ ๋™๊ธฐ์‹ ํ•จ์ˆ˜์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋ณด์ด์ง€๋งŒ, ๊ทธ ์ด๋ฉด์€ callbacks์œผ๋กœ ๊ฐ€๋“ ์ฑ„์›Œ์ง„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋น„๋™๊ธฐ์  ์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ ์ง์ž‘ํ•˜๊ฒ ์ง€๋งŒ, await์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ํ•˜์—ฌ๊ธˆ ๋‚ด๋ถ€์ ์œผ๋กœ then() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

// example 3.1
โ€‹
const p = {
then: (onFulfilled) => {
// ๋‹ค์€์€ "then(): function () { [native code] }"๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
console.log("then():", onFulfilled.toString());
// ์˜ค๋ฅ˜ ์Šคํƒ์—๋Š” ํ•œ๊ฐ€์ง€ ์—”ํŠธ๋ฆฌ๋ฉด ๋‚˜์˜ต๋‹ˆ๋‹ค.
// Error
// at Object.then (/examples/chapter3.test.js:8:21)
console.log(new Error().stack);
onFulfilled("Hello, World!");
},
};
console.log(await p); // "Hello, World!"๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

await ํ‚ค์›Œ๋“œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ํ•˜์—ฌ๊ธˆ ์ด๋ฒคํŠธ ๋ฃจํ”„์˜ ๋‹ค์Œ ์ฐจ๋ก€๊นŒ์ง€ ์‹คํ–‰์„ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์—์„œ await ์ดํ›„, console.log()๋Š” ++currentId ์ฝ”๋“œ ๋’ค์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

// example 3.2
โ€‹
const startId = 0;
let currentId = 0;
โ€‹
process.nextTick(() => ++currentId);
โ€‹
const p = {
then: (onFulfilled) => {
console.log("then():", currentId - startId); // "then(): 1"
onFulfilled("Hello, World!");
},
};
โ€‹
console.log("Before:", currentId - startId); // "Before: 0"
await p;
console.log("After:", currentId - startId); // "After: 1"

๋น„๋ก then()์ด ์™„์ „ ๋™๊ธฐ์‹์ด๋ผ๋„ ๋‹ค์Œ ์ฐจ๋ก€์— ์ž‘๋™๋œ๋‹ค๋Š” ์ ์„ ์œ ์˜ํ•˜์„ธ์š”. ์ด๊ฒƒ์€ await๋Š” ์ตœ์†Œํ•œ ๋‹ค์Œ ์ฐจ๋ก€๊นŒ์ง€๋Š” ์‹คํ–‰์„ ์ผ์‹œ ์ •์ง€ ์‹œํ‚จ๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ onRejected(err)๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด await ํ‚ค์›Œ๋“œ๋Š” ์—ฌ๋Ÿฌ๋ถ„ ํ•จ์ˆ˜ ๋ณธ์ฒด๋กœ err๋ฅผ throwsํ•ฉ๋‹ˆ๋‹ค.

// example 3.3
โ€‹
const startId = 0;
let currentId = 0;
โ€‹
process.nextTick(() => ++currentId);
โ€‹
const p = {
then: (onFulfilled, onRejected) => {
console.log("then():", currentId - startId); // "then(): 1
return onRejected(Error("Oops!"));
},
};
โ€‹
try {
console.log("Before:", currentId - startId); // "Before: 0"
await p;
console.log("This does not print");
} catch (error) {
console.log("After:", currentId - startId); // "After: 1"
}

await vs return

async ํ•จ์ˆ˜์—์„œ return์€ ๊ทธ ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” promise๋ฅผ "ํ•ด๊ฒฐ" ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์„ธ์š”. ์ด๊ฒƒ์€ ์—ฌ๋Ÿฌ๋ถ„์ด promise๋„๋ฅผ return ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. await์™€ return์˜ ์ฐจ์ด์ ์€ ๋ฌด์—ˆ์„๊นŒ์š”? ๋‹ต์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค. promise์— await ํ•  ๊ฒฝ์šฐ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๊ทธ async ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ์ผ ์‹œ์ •์ง€์‹œํ‚จ ํ›„์— ์žฌ๊ฐœ์‹œํ‚ค์ง€๋งŒ, promise๋ฅผ return ํ•œ๋‹ค๋ฉด ๊ทธ async ํ•จ์ˆ˜์˜ ์ˆ˜ํ–‰์„ ์ข…๋ฃŒํ•ด ๋ฒ„๋ฆฌ๊ณ  return๋œ ํ•จ์ˆ˜๋ฅผ ์žฌ๊ฐœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ต์€ ๋ถ„๋ช… ๋งž์ง€๋งŒ, await ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ์•„๋‚ด๊ธฐ์—๋Š” ์˜๋ฏธ๊ฐ€ ์ข€ ํ•จ์ถ•์ ์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์ด await p๋ฅผ try/catch๋กœ ๊ฐ์‹ธ๊ณ  p๋ฅผ "๊ฑฐ์ ˆ" ์ฒ˜๋ฆฌํ•˜๋ฉด error๋ฅผ catchํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Œ€์‹  return p ํ•œ๋‹ค๋ฉด ์–ด๋–จ๊นŒ์š”?

// example 3.4
โ€‹
async function test() {
try {
return Promise.reject(new Error("Oops!"));
} catch (error) {
return "ok";
}
}
โ€‹
// "Oops!"๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.
test().then(
(v) => console.log(v),
(err) => console.log(err.message)
);

try/catch๋Š” ์—ฌ๋Ÿฌ๋ถ„์ด returnํ•œ "๊ฑฐ์ ˆ"ํ•œ promise๋ฅผ catch ํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ์ ์— ์ฃผ๋ชฉํ•˜๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. promise๊ฐ€ "๊ฑฐ์ ˆ"์ผ ๋•Œ, ์™œ await ๋งŒ์ด error๋ฅผ catch ํ• ๊นŒ์š”? ๊ทธ๊ฒƒ์€ await๋Š” ์‹คํ–‰์„ ์žฌ๊ฐœํ•  ๋•Œ error๋ฅผ throw ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. promise๋ฅผ return ํ•˜๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ async ํ•จ์ˆ˜ ๋ณธ์ฒด์˜ ์‹คํ–‰์„ ๋ฉˆ์ถ”๊ณ  ์ด async ํ•จ์ˆ˜์˜ promise์— ๋Œ€ํ•ด resolve()๋ฅผ ์ˆ˜ํ–‰ํ•ด ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด์— promise์— await์„ ํ•˜๋ฉด, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๊ทธ async ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ค๊ณ  ๊ทธ promise๊ฐ€ "์ •์ฐฉ"๋˜๋ฉด ๋‹ค์‹œ ์žฌ๊ฐœํ•ฉ๋‹ˆ๋‹ค. await ์ดํ›„, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์˜ async ํ•จ์ˆ˜๋ฅผ ์žฌ๊ฐœํ•  ๋•Œ, ๋งŒ์•ฝ await ํ•œ promise๊ฐ€ "๊ฑฐ์ ˆ" ์ƒํƒœ๋ฉด ๊ทธ ๊ทธ ๊ฑฐ์ ˆ๊ฐ’์ธ error๋ฅผ ๋˜์ง€๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” promise์— await ํ•  ๊ฒฝ์šฐ ์–ด๋–ค ์ผ์ด ๋ฐœ์ƒํ•˜๋Š”์ง€ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด์—, aync ํ•จ์ˆ˜์—์„œ promise๋ฅผ return ํ•  ๋•Œ, ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ทธ promise๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ํ™˜๊ฒฝ์œผ๋กœ ๊ฐ€๋ฒ„๋ฆฌ๊ณ  ๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ์•„์˜ค์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ example 3.4์—์„œ try/catch๊ฐ€ error๋ฅผ ๋‹ค๋ฃจ์ง€ ๋ชปํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ๋“ค์€ error๋ฅผ catchํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๋Œ€์•ˆ๋“ค์ž…๋‹ˆ๋‹ค. example 3.5๋Š” await p ๊ฒฐ๊ณผ๋ฅผ v ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๊ณ  ๊ทธ ๋ณ€์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  example 3.6์€ return await๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.

// example 3.5
โ€‹
async function test() {
try {
const v = await Promise.reject(new Error("Oops!"));
return v;
} catch (error) {
return "ok";
}
}
โ€‹
// Prints "ok"
test().then(
(v) => console.log(v),
(err) => console.log(err.message)
);
// example 3.6
โ€‹
async function test() {
try {
return await Promise.reject(new Error("Oops!"));
} catch (error) {
return "ok";
}
}
โ€‹
// Prints "ok"
test().then(
(v) => console.log(v),
(err) => console.log(err.message)
);

๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ๋ฒ• ๋ชจ๋‘ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ example 3.5๊ฐ€ ์ข€ ๋” ๋‹จ์ˆœํ•˜๊ณ  ํ˜ผ๋ž€์ด ์ ์Šต๋‹ˆ๋‹ค. return await์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋น„์ „๋ฌธ๊ฐ€์—๊ฒŒ๋Š” ๋‹ค์†Œ ์–ด๋ ต๊ฒŒ ๋Š๊ปด์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ผ๋ฐ˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž๋Š” ๋ชฉ์ ์—์„œ ์‚ด์ง ๋ฒ‹์–ด๋‚ฉ๋‹ˆ๋‹ค.

Concurrency: ๋™์‹œ์„ฑ

์ด์  , await p๊ฐ€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ํ•˜์—ฌ๊ธˆ ์—ฌ๋Ÿฌ๋ถ„์˜ async ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ค๊ณ  p.then()๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  promise๊ฐ€ "์ •์ฐฉ"๋  ๋•Œ ์ˆ˜ํ–‰์„ ์žฌ๊ฐœํ•œ๋‹ค๋Š” ๊ฒƒ๋„์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์—ฌ๋Ÿฌ async ํ•จ์ˆ˜๋“ค์ด ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰๋  ๋•Œ๋Š” ์ด๊ฒƒ์€ ์–ด๋–ค ์˜๋ฏธ๋ฅผ ๊ฐ€์งˆ๊นŒ์š”?

"์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹จ์ผ ์Šค๋ ˆ๋“œ ๋ฐฉ์‹์ด๋‹ค" ๋ผ๋Š” ๊ฐœ๋…์€ ๋ณดํ†ต์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๊ฐ€ ์ˆ˜ํ–‰ ์ค‘์ผ ๋•Œ, ๋‹ค๋ฅธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์–ธ์–ด๋“ค ์—์„œ setImmediate() ๊ฐ™์€ ๊ตฌ์กฐ๋Š” ๋ถ„๋ฆฌ๋œ ์Šค๋ ˆ๋“œ์—์„œ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š” ๋™์•ˆ์—๋„ ์ถœ๋ ฅ์„ ํ•˜์ง€๋งŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์ด๊ฒƒ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

// example 3.7
โ€‹
setImmediate(() => console.log("Hello, World!"));
โ€‹
// ๋‹ค์Œ์€ ๋ฌดํ•œ ๋ฃจํ”„์— ๋น ์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฒคํŠธ ๋ฃจํ”„๋กœ ๋Œ์•„๊ฐ€์ง€ ๋ชปํ•˜๋ฏ€๋กœ
// ์œ„ ์ฝœ๋ฐฑ์˜ console.log๋Š” ๊ฒฐ์ฝ” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
while (true) {}

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋“ค์€ ๋ฌผ๋ฆฌํ•™์˜ "ํŒŒ์šธ๋ฆฌ์˜ ๋ฒ ํƒ€์›๋ฆฌ"์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ๋‘ ๊ฐœ์˜ ์ผ๋ฐ˜์ ์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋“ค๋„ ๋™์‹œ์— ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์—์„œ ๋™์ž‘ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํด๋กœ์ ธ(์ฝœ๋ฐฑ)๋“ค์€ ๋ถ„๋ฆฌ๋œ ํ•จ์ˆ˜๋“ค์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์•„๋ž˜ foo(), bar() ๊ทธ๋ฆฌ๊ณ  baz() ํ•จ์ˆ˜๋“ค์€ ๋ชจ๋‘ ๋ถ„๋ฆฌ๋˜์–ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

// example 3.8
โ€‹
function foo() {
let x = 0;
โ€‹
// foo()๋ฅผ ๋งˆ์น˜๊ณ , bar()๊ฐ€ ํ›„์— ์‹คํ–‰๋˜๋”๋ผ๋„ ์—ฌ์ „ํžˆ x๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
setImmediate(bar);
โ€‹
// baz()๊ฐ€ ๋งˆ์น  ๋•Œ๊นŒ์ง€ foo() ์‹คํ–‰์ด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค
baz();
โ€‹
function bar() {
++x;
}
function baz() {
++x;
}
}

๋น„๋™๊ธฐ ํ•จ์ˆ˜๋“ค๋„ ๊ฐ™์€ ๊ทœ์น™์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค: ์–ด๋–ค ๋‘ ํ•จ์ˆ˜๋„ ๋™์‹œ์— ๋™์ž‘ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฉ”๋ชจ๋ฆฌ๋งŒ ๋ถ€์กฑํ•˜์ง€ ์•Š๋‹ค๋ฉด ์–ด๋–ค ์ˆ˜์˜ async ํ•จ์ˆ˜๋“ค๋„ ๋™์‹œ์— ์ผ์‹œ ์ •์ง€ ๋  ์ˆ˜ ์žˆ๊ณ  async ํ•จ์ˆ˜๊ฐ€ ์ผ์‹œ ์ •์ง€๋  ๋•Œ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋“ค์ด ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// example 3.9
โ€‹
run().catch((error) => console.error(error.stack));
โ€‹
async function run() {
// await ํ•  ๋•Œ๋งˆ๋‹ค run()์ด ์ผ์‹œ์ •์ง€๋˜๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜ ์ฝœ๋ฐฑ์ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.
setImmediate(() => console.log("Hello, World!"));
โ€‹
// ๋ฃจํ”„๋ฅผ ๋Œ ๋•Œ ๋งˆ๋‹ค, run() ํ•จ์ˆ˜๊ฐ€ ์ผ์‹œ์ •์ง€ ๋˜๊ณ  ๋‹ค์Œ tick์— ์žฌ๊ฐœ๋ฉ๋‹ˆ๋‹ค.
while (true) {
await new Promise((resolve) => setImmediate(resolve));
}
}

์ด ๋ฐฉ์‹์„ ์ด์šฉํ•˜๋ฉด ์˜ค๋žœ ์‹œ๊ฐ„์ด ์†Œ์š”๋˜๋Š” ๋™๊ธฐ ํ•จ์ˆ˜๋“ค์„ async ํ•จ์ˆ˜๋“ค์„ ํ†ตํ•ด ๋‹จ์ˆœํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋งค์šฐ ํฐ ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜ ๋‘ ๊ฐœ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•˜๊ณ ์ž ํ•  ๋•Œ, async/await ์ด ์—†๋‹ค๋ฉด ํŽธ๋ฒ•์ ์ธ ์žฌ๊ท€๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜์ง€๋งŒ async/await๋Š” ์ด ์ฒ˜๋ฆฌ๋ฅผ ํ‰๋ฒ”ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

// example 3.10
โ€‹
await Promise.all([fibonacci(50000), fibonacci(50000)]);
โ€‹
async function fibonacci(n) {
let [prev2, prev1, cur] = [1, 1, 1];
โ€‹
for (let i = 2; i < n; ++i) {
// ๋‹ค๋ฅธ ํ˜ธ์ถœ์ด ์ง„ํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก fibonacci()๋ฅผ ์ผ์‹œ์ •์ง€ ์‹œํ‚ต๋‹ˆ๋‹ค
await new Promise((resolve) => setImmediate(resolve));
โ€‹
// "Fib: 10000"
// "Fib: 10000"
// "Fib: 20000" ...
if (i % 10000 === 0) console.log("Fib:", i);
cur = prev1 + prev2;
prev2 = prev1;
prev1 = cur;
}
return cur;
}

์ด ์˜ˆ๋Š” ๋‹จ์ˆœํ•˜์ง€๋งŒ ์ž์—ฐ์Šค๋Ÿฝ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์ข€ ๋” ํ˜„์‹ค์ ์ธ ์‚ฌ๋ก€๋Š” ํด๋Ÿฌ์Šคํ„ฐ๋ง ๊ฐ™์ด ์ž ์žฌ์ ์œผ๋กœ ๋งค์šฐ ๋น„์‹ผ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ˆ˜ํ–‰ํ•˜๋Š” Express API ์ฒ˜๋ฆฌ์ž…๋‹ˆ๋‹ค. ๋ณธ์ธ์€ ์ด๋Ÿฌํ•œ ํŒจํ„ด์„ ์ƒ์šฉ Express API์—์„œ ์‚ฌ์šฉํ•œ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ผ์šฐํŠธ๋“ค์„ ๋ด‰์‡„ํ•˜์ง€ ์•Š๊ณ  ํ•œ ๋ผ์šฐํŠธ์—์„œ O(n^5) ํด๋Ÿฌ์Šคํ„ฐ๋ง ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ์ ์ธ ์‚ฌํ•ญ์€ ์—ฌ๋Ÿฌ๋ถ„์ด await ์œผ๋กœ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ค๊ฑฐ๋‚˜ return ๋˜๋Š” throw๋ฅผ ํ†ตํ•ด ํ•จ์ˆ˜๋ฅผ ์ข…๋ฃŒํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด async ํ•จ์ˆ˜๋Š” ์–ด๋–ค ๊ฐ„์„ญ ์—†์ด ์ฒ˜๋ฆฌ๋  ๊ฒƒ์ด๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ƒ์‹์ ์œผ๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์—ฌ์ „ํžˆ ๋‹จ์ผ ์“ฐ๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๊ฐœ์˜ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋Š” ๋™์‹œ์— ๋™์ž‘ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ await๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ๋ถ„์˜ async ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ด์œผ๋กœ์จ, ์ด๋ฒคํŠธ ๋ฃจํ”„๋กœ ์ง„์ž…ํ•˜๊ฒŒ ํ•˜์—ฌ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋“ค์ด ์ˆ˜ํ–‰๋  ๊ธฐํšŒ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Async/Await vs Generators

async/await๋Š” generators์™€ ๊ณตํ†ต์ ์ด ๋งŽ์Šต๋‹ˆ๋‹ค. generators๋Š” 2015๋…„๋„ํŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ทœ๊ฒฉ์˜ ํŠน์ง•์ž…๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ ํ•จ์ˆ˜์™€ ๊ฐ™์ด, generator ํ•จ์ˆ˜๋„ ์ผ์‹œ ์ •์ง€๋˜๊ณ  ํ›„์— ์žฌ๊ฐœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ, async ํ•จ์ˆ˜์™€ generators ํ•จ์ˆ˜ ์‚ฌ์ด์—๋Š” ๋‘ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์ฐจ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. generator ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” await์ด ์•„๋‹Œ yield ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  2. generator ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ค๋ฉด ์ œ์–ด๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„(์ธํ„ฐํ”„๋ฆฌํ„ฐ)๋กœ ๊ฐ€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค. generator ๊ฐ์ฒด์— next()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ generator ํ•จ์ˆ˜๋ฅผ ์žฌ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

yield, next() ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ generator ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ค๊ฑฐ๋‚˜ ์žฌ๊ฐœํ•˜๋Š” ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.

// example 3.11
โ€‹
// The `function*`ํ‘œํ˜„์œผ๋กœ ์ผ๋ฐ˜ํ•จ์ˆ˜๋ฅผ generator() ํ•จ์ˆ˜๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
const generatorFunction = function* () {
console.log("Step 1");
yield 1;
console.log("Step 2");
yield 2;
console.log("Done");
};
โ€‹
// generator ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’์€ generator ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
// generator ํ•จ์ˆ˜๋Š” ์—ฌ๋Ÿฌ๋ถ„์ด next()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
const generatorObject = generatorFunction();
let yielded = generatorObject.next(); // Prints "Step 1"
โ€‹
console.log(yielded.value); // Prints "1"
yielded = generatorObject.next(); // Prints "Step 2"
โ€‹
console.log(yielded.value); // Prints "2"
generatorObject.next(); // Prints "Done"

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ์˜ ํ™œ์šฉํ•˜๋ฉด generator ๋„ async/await์™€ ์‚ฌ์‹ค์ƒ ๋™์ผํ•œ ํŒจํ„ด์œผ๋กœ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ž˜ ์•Œ๋ ค์ง„ ๋™์‹œ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” 'co'์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ์€ async/await ๋Œ€์‹ ์— 'co'๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.

// example 3.12
โ€‹
const co = require("co");
โ€‹
// co.wrap()์€ generator๋ฅผ async ํ•จ์ˆ˜์ฒ˜๋Ÿผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.
const runCo = co.wrap(function* () {
// 1์ดˆํ›„ 'Hello, World!'๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
yield new Promise((resolve) => setTimeout(() => resolve(), 1000));
console.log("Hello, World!");
});
โ€‹
// In particular, wrapped functions return a promise
runCo().catch((error) => console.log(error.stack));

Co๋Š” async/await๊ฐ€ ํƒœ์ƒ์ ์œผ๋กœ ์ง€์›ํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๊น”๋”ํ•œ ํŠน์ง•๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๋Š” ๋•๋ถ„์—, co๋Š” ์ข€ ๋” ํ™•์žฅ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, co๋Š” ์—ฌ๋Ÿฌ๋ถ„์ด ์–ธ์ œ promise ๋ฐฐ์—ด์ด๋‚˜ promise ๋งต์„ yieldํ•  ์ง€ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// example 3.13
โ€‹
const runCo = co.wrap(function* () {
const p1 = Promise.resolve("Hello");
const p2 = Promise.resolve("World");
โ€‹
// Co๋Š” promise ๋ฐฐ์—ด์ด๋‚˜ promise ์†์„ฑ์„ ๊ฐ–๋Š” ๊ฐ์ฒด๋“ค์˜ ๋ฐฐ์—ด์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// async/await ๋ผ๋ฉด, promise ๋ฐฐ์—ด์— await์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”
// Promise.all()์„ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
console.log(yield [p1, p2]); // [ 'Hello', 'World' ]
console.log(yield { p1, p2 }); // { p1: 'Hello', p2: 'World' }
});

co์˜ ๋ฌต์‹œ์  promise ๋ณ€ํ™˜์˜ ๋‹จ์ ์€ promise๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋Š” ์–ด๋–ค ๊ฒƒ์„ yield ํ•  ๋•Œ error๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์  ์ž…๋‹ˆ๋‹ค.

// example 3.14
โ€‹
const runCo = co.wrap(function* () {
// 'TypeError: You may only yield a function, promise, generator,
// array, or object but the following object was passed: "1"'
yield 1;
});

์‹ค์ œ, co๊ฐ€ yield 1์„ ์˜ค๋ฅ˜๋กœ ๋‹ค๋ฃฌ๋‹ค๋Š” ๊ฒƒ์€ ์ˆ˜๋งŽ์€ ์˜ค๋ฅ˜๋“ค์„ catchํ•˜๋Š”๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งŽ์€ ๋ถˆํ•„์š”ํ•œ ์˜ค๋ฅ˜๋ฅผ ์•ผ๊ธฐํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. async/await์—์„œ, await 1์€ ์ •์ƒ์ด๊ณ  ๊ฐ’ 1๋กœ ํ‰๊ฐ€ํ•˜๋ฉฐ ์ด๊ฒƒ์ด ์ข€ ๋” ๊ฒฌ๊ณ ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค.

Async/await๋Š” co, generator๋ฅผ ๋›ฐ์–ด๋„˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์žฅ์ ๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ํฐ ๊ฒƒ์€async/await๋Š” node.js๋ฅผ ๋น„๋กฏํ•ด ํ˜„ ์‹œ๋Œ€์˜ ๋ธŒ๋ผ์šฐ์ €๋“ค์ด ๊ธฐ๋ณธ์œผ๋กœ ์ง€์›ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ co ๊ฐ™์ด ๋ณ„๋„์˜ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ async/await๋Š” ํ›จ์”ฌ ๊น”๋”ํ•œ ์Šคํƒ ์ถ”์  ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. co์˜ ์Šคํƒ ์ถ”์ ์€ ์˜ค๋ฅ˜ ํŒŒ์•…์„ ๋ชจํ˜ธํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์ˆ˜๋งŽ์€ generator.next()์™€ onFulfilled ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

// example 3.15
โ€‹
const runCo = co.wrap(function* () {
yield new Promise((resolve) => setImmediate(resolve));
throw new Error("Oops!");
});
โ€‹
// Error: Oops!
// at /test.js:3:9
// at Generator.next (<anonymous>)
// at onFulfilled (/node_modules/co/index.js:65:19)
// at <anonymous>
runCo().catch((error) => console.log(error.stack));

๋น„์Šทํ•œ ์ƒํ™ฉ์—์„œ, async/await ์Šคํƒ ์ถ”์ ์€ ํ•จ์ˆ˜๋ช…์€ ๋‚˜์˜ค์ง€๋งŒ generator.next(), onFulfilled ๋“ฑ์€ ์ถœ๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Async/await์˜ onFulfilled์€ ์‚ฌ์šฉ์ž ๋ชจ๋“œ๊ฐ€ ์•„๋‹Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ๋ชจ๋“œ์—์„œ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

// example 3.16
โ€‹
async function runAsync() {
await new Promise((resolve) => setImmediate(resolve));
throw new Error("Oops!");
}
โ€‹
// Error: Oops!
// at runAsync (/home/val/test.js:5:9)
// at <anonymous>
runAsync().catch((error) => console.log(error.stack));

์ผ๋ฐ˜์ ์œผ๋กœ, async/await๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋‚ด๋ถ€์—์„œ ๊ธฐ๋ณธ ์ง€์›๋˜๊ณ , ๋ถˆํ•„์š”ํ•œ ์˜ค๋ฅ˜ ๋“ค์„ ์ ๊ฒŒ ์ผ์œผํ‚ค๋ฉฐ ์—ฌ๋Ÿฌ๋ถ„์ด ํ•„์š”๋กœ ํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. Co๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฌธ๋ฒ•์ ์œผ๋กœ ๊น”๋”ํ•œ ํŽธ์ด์„ฑ์„ ์ œ๊ณตํ•˜๊ณ  ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ €์—์„œ๋„ ๋™์ž‘ํ•˜์ง€๋งŒ, ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์„ ์ •๋‹นํ™” ํ•  ๋งŒํผ ์ถฉ๋ถ„ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

Core Principles: ํ•ต์‹ฌ ์›๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ async ํ•จ์ˆ˜๊ฐ€ ์ผ์‹œ ์ •์ง€ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ์–ด๋–ค ์˜๋ฏธ ์ธ์ง€์— ๋Œ€ํ•ด ์ž์„ธํžˆ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž์‹ ์˜ ์•ฑ์— async/await๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์ผ๊นŒ์š”?

promise๊ฐ€ ๋  ์ˆ˜ ์—†๋Š” ๊ฐ’์— await ํ•˜์ง€ ๋งˆ์„ธ์š”.

await 1์ด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ๊ทธ๋ ‡๊ฒŒ ํ•ด์•ผ๋งŒ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๋งŽ์€ async/await์˜ ์ดˆ๋ณด์ž๋“ค์€ await์„ ์˜ค์šฉํ•˜๊ฑฐ๋‚˜ ๋‚จ์šฉํ•˜๊ณค ํ•ฉ๋‹ˆ๋‹ค.

// example 3.17
โ€‹
async function findSubstr(arr, str) {
// Don't do this! There's no reason for this function to be async
// ์ด๋Ÿฐ๊ฑฐ ํ•˜์ง€ ๋งˆ์„ธ์š”. ์ด ํ•จ์ˆ˜๊ฐ€ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์—ฌ์•ผํ•  ์ด์œ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
for (let i = await 0; i < arr.length; ++i) {
if (await arr[i].includes(str)) return arr[i];
}
}

์ผ๋ฐ˜์ ์œผ๋กœ, promise๊ฐ€ ๋  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€๋˜๋Š” ๊ฐ’์— ๋Œ€ํ•ด์„œ await์„ ์‚ฌ์šฉํ•˜์„ธ์š”. ๊ฒฐ์ฝ” promise๊ฐ€ ๋  ์ˆ˜ ์—†๊ฑฐ๋‚˜, promise ์ผ ์ˆ˜๋„ ์žˆ๋‹ค๊ณ  ๊ฑฐ์ง“์œผ๋กœ ์•”์‹œํ•˜๋Š” ๊ฐ’์—๋Š” await์„ ํ•  ์ด์œ ๋Š” ์—†์Šต๋‹ˆ๋‹ค.

findSubstr() ํ•จ์ˆ˜๋ฅผ async๋กœ ๋งŒ๋“œ๋Š” ์œ ์ผํ•œ ์ด์œ ๋Š” ์‹คํ–‰์„ ์ผ์‹œ ์ •์ง€ ์‹œ์ผœ์„œ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋“ค๋กœ ํ•˜์—ฌ๊ธˆ example 3.10 ์ฒ˜๋Ÿผ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•  ๋•Œ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ findSubstr()๊ฐ€ ๋Œ€๋Ÿ‰์˜ ๋ฐฐ์—ด์„ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ํ•  ๋•Œ๋‚˜ ์œ ์šฉํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ, ๋‹ค๋ฅธ ์ž‘์—…๋“ค์ด ์ฒ˜๋ฆฌ๋  ๊ธฐํšŒ๋ฅผ ๊ฐ–๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด await new Promise(setImmediate)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

await์„ ์›ํ•˜๋Š” ์–ด๋–ค ๊ฐ’๋“ค๋„ ์ผ๋‹จ, promise๋กœ ๋ฐ”๊ฟ” ๋†“์•„์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋“ค ๋“ค์–ด, ๋ณ‘๋ ฌ๋กœ ๋ณต์ˆ˜์˜ promise๋“ค์„ awaitํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Promise.all()์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// example 3.18
โ€‹
async function run() {
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
โ€‹
// 'arr1'์€ array์ด์ง€ promise๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ.
const arr1 = await [p1, p2];
โ€‹
// arr1์€ ๋ฐฐ์—ด์ด๊ธฐ ๋•Œ๋ฌธ์— promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋„์šฐ๋ฏธ ํ•จ์ˆ˜ Promise.all์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
const arr2 = await Promise.all(arr1);
}

Promise๊ฐ€ ์•„๋‹ˆ๋ฉด ๊ทธ๋ƒฅ return์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

example 3.4์—์„œ ๋ณด๋“ฏ์ด, async ํ•จ์ˆ˜๋Š” promise๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‹ค์†Œ ๋œปํ•˜์ง€ ๋ชปํ•œ ์ƒํ™ฉ์„ ๋งž์Šต๋‹ˆ๋‹ค. ํ•ด๊ฒฐ๋œ ๊ฐ’์œผ๋กœ promise๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹ ์— ๊ฐ’์œผ๋กœ ํ•ด๊ฒฐ๋˜๋„๋ก await์„ ์‚ฌ์šฉํ•˜๊ณ  ๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ ๊ทธ ๊ฒฐ๊ณผ๊ฐ’(ํ•ด๊ฒฐ๊ฐ’)์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. async์™€ return ์‚ฌ์ด์˜ ์ฐจ์ด๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ๋ณด๋‹ค๋Š” await๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋‚˜์„œ ์ •์ฐฉ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์œผ๋กœ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค.

// example 3.19
โ€‹
async function fn1() {
// ๊ดœ์ฐฎ์ง€๋งŒ, example 3.4์—์„œ ๋ณด์•˜๋“ฏ์ด try/catch ์‚ฌ์šฉ์‹œ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๋Š”
// ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
return asyncFunction();
}
โ€‹
async function fn2() {
// ๋ช‡ ์ค„ ๋” ๋“ค์–ด๊ฐ€๊ธด ํ•˜์ง€๋งŒ ๋ฌธ์ œ ์†Œ์ง€๊ฐ€ ์ ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ
// asyncFunction()์˜ ์˜ค๋ฅ˜๋“ค์„ ์ฒ˜๋ฆฌํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์ด ๋ฐฉ๋ฒ•์ด ์ข‹๋‹ค.
const ret = await asyncFunction();
return ret;
}

forEach(), map() ๊ฐ™์€ ๋ฐฐ์—ด ๋„์šฐ๋ฏธ ํ•จ์ˆ˜์—๋Š” await ์‚ฌ์šฉ๋ณด๋‹ค loop ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

async ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋งŒ await์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, forEach() ๊ฐ™์€ ํ•จ์ˆ˜ํ˜• ๋ฐฐ์—ด ํ•จ์ˆ˜์—์„œ async ํ•จ์ˆ˜๋“ค์€ sync ํ•จ์ˆ˜๋“ค๊ณผ๋Š” ๋‹ค๋ฅธ ํ–‰ํƒœ๋ฅผ ๋ณด์ž…๋‹ˆ๋‹ค.

// example 3.20
โ€‹
async function test() {
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
โ€‹
// SyntaxError: Unexpected identifier
[p1, p2].forEach(p => { await p; });
}

ํ•„์š”ํ•œ ๊ฒƒ์ด๋ผ ๊ณค async ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ผ๊ณ  ์ƒ๊ฐํ•  ์ง€ ๋ชจ๋ฅด์ง€๋งŒ, await์„ ํ•ด๋„ test()๋Š” ์ผ์‹œ ์ •์ง€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

// example 3.21
โ€‹
async function test() {
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
โ€‹
// ๋ฐฐ์—ด ๊ฐœ์ˆ˜ ๋งŒํผ, 2๊ฐœ์˜ async ํ•จ์ˆ˜๋ฅผ ์ค€๋น„ํ–ˆ์ง€๋งŒ test()๋ฅผ ์ผ์‹œ ์ •์ง€์‹œํ‚ค์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
// await p๋กœ ์ •์ง€๋˜๋Š” ๊ฒƒ์€ test()๊ฐ€ ์•„๋‹Œ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
[p1, p2].forEach(async (p) => {
console.log(await p);
});
โ€‹
// '1', '2' ๋ณด๋‹ค ๋จผ์ € 'Done'์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์™œ๋‚ดํ•˜๋ฉด await p๋Š” test()๊ฐ€ ์•„๋‹Œ
// ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ์ •์ง€ ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
console.log("Done");
}

์˜ค๋ฅ˜๋Š” .catch()๋กœ ์ฒ˜๋ฆฌํ•˜์„ธ์š”.

ํ†ตํ•ฉ๋œ ์˜ค๋ฅ˜ ๊ด€๋ฆฌ๋Š” async/await์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ํŠน์ง•๋“ค ์ค‘ ํ•˜๋‚˜ ์ž…๋‹ˆ๋‹ค. asyncFn().catch()๋Š” asyncFn() ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ชจ๋“  ์˜ค๋ฅ˜ ๋“ค(๋™๊ธฐ ๋˜๋Š” ๋น„๋™๊ธฐ ์˜ค๋ฅ˜)์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. example 3.4: try/catch๋Š” p๊ฐ€ ์‹คํŒจ๋ฉด return p๋กœ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜ ๋“ค์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ–ˆ๋˜ ๊ฒƒ์„ ๊ธฐ์–ตํ•˜๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

// example 3.22
โ€‹
async function fn1() {
// p ๊ฐ€ rejeced ์ผ ๋•Œ, return p๋กœ ๊ทธ ์˜ค๋ฅ˜๋ฅผ ์žก์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.
try {
/* Complex logic */
} catch (err) {
handleError(err);
}
}
โ€‹
async function fn2() {
/* Complex logic */
}
โ€‹
// ์•„์ฃผ ์ข‹์Šต๋‹ˆ๋‹ค~~
fn2().catch(handleError);

์ผ๋ฐ˜์ ์œผ๋กœ, async ํ•จ์ˆ˜์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์˜ค๋ฅ˜๋Š” .catch()์—์„œ ์žก์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. .catch()๊ฐ€ ์—†๋Š” async/await ์ฝ”๋“œ๋ฅผ ๋ณด๊ฒŒ ๋œ๋‹ค๋ฉด ์–ด๋”˜๊ฐ€ ์ฒ˜๋ฆฌ๋˜์ง€ ๋ชปํ•  ์˜ค๋ฅ˜๊ฐ€ ์กด์žฌํ•˜๊ฒŒ ๋˜๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹ฌํ•˜์„ธ์š”. ์ข‹์€ async/await ์ฝ”๋“œ๋Š” ๋ชจ๋“  async ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด .catch()๋กœ ์žกํž ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” wrap() ํ•จ์ˆ˜ ๊ฐ™์ด ์ง‘์ค‘ํ™”๋œ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// example 3.23
โ€‹
const wrap = (fn) =>
function () {
// ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ๊ฐ–๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
return fn.apply(null, arguments).catch((error) => console.log(error));
};
โ€‹
const [fn1, fn2] = [
async function () {
throw Error("err1");
},
async function () {
throw Error("err2");
},
].map(wrap);
โ€‹
fn1(); // Prints "err1"
fn2(); // Prints "err2"

Exercise 1: Implementing Custom Thenables

๋งž์ถคํ˜• thenable์„ ๊ตฌํ˜„ํ•ด ๋ด…์‹œ๋‹ค.

Example 3.2์—์„œ ๋ณด์•˜๋“ฏ์ด, ์–ด๋–ค ๊ฐ์ฒด๋ฅผ async/await์™€ ์—ฎ๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ฒƒ์€ then() ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. await ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ then()์„ ํ˜ธ์ถœํ•˜๊ณ  onFulfilled() ๋˜๋Š” onRejected()๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๊นŒ์ง€ ํ˜„์žฌ async ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์—ฌ๋Ÿฌ๋ถ„์ด ์–ด๋–ค ๊ฐ์ฒด๋ผ๋„ then() ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ๊ทธ ๊ฐ์ฒด๊ฐ€ async/await์™€ ์ƒํ˜ธ ์ž‘๋™ ํ•˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

superagent ๊ฐ™์€ ๋งŽ์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ HTTP ๋ชจ๋“ˆ๋“ค์€ ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋“ค๋กœ request๋“ค์„ ์ฒ˜๋ฆฌํ•ด ๋‚˜๊ฐ€๋„๋ก chainable API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

superagent.get(url).set('API-Key', 'test').end((err, res) => { /* Handle response */ });

์•„๋ž˜ HTTPRequests ํด๋ž˜์Šค๋Š” chainable API๋ฅผ ๊ฐ€์ง„ ๋‹จ์ˆœํ™”๋œ HTTP ๋ชจ๋“ˆ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ˜„์žฌ๋Š” exec() ํ•จ์ˆ˜๋ฅผ ๊ฒฝ์œ ํ•˜๋Š” ์ฝœ๋ฐฑ๋“ค ๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. then() ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ ์ด HTTPRequests ํด๋ž˜์Šค๊ฐ€ async/await์™€ ์ž‘๋™ํ•˜๋„๋ก ํ•ด๋ณด์„ธ์š”.

์•„๋ž˜๋Š” ์ดˆ๊ธฐ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•ด์„œ node.js๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋ณด์„ธ์š”. ๋˜๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €๋กœ codepen ์‚ฌ์ดํŠธ(http://bit.ly/async-await-exercise-31)์— ์ ‘์†ํ•ด์„œ ์™„์„ฑํ•ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

class HTTPRequest {
// ์˜ค์ง ์•„๋ž˜ then() ๋งŒ ์ˆ˜์ •ํ•˜์„ธ์š”.
static create() {
return new HTTPRequest();
}
get(url) {
this.method = "get";
this.url = url;
return this;
}
exec(callback) {
fetch(this.url, this)
.then((res) => res.json())
.then((res) => callback(null, res))
.catch(callback);
}
then(onFulfilled, onRejected) {
throw new Error("Not Implemented"); // Implement this function
}
}
โ€‹
// Don't modify the below code
run().catch((error) => console.error(error.stack));
async function run() {
const url =
"https://" + "us-central1-mastering-async-await.cloudfunctions.net/posts";
const res = await HTTPRequest.create().get(url);
console.log("Success", res[0].id === 51);
}

Exercise 2: Async forEach()

Example 3.21 ๊ฐ™์ด, forEach ๋ฐฐ์—ด ํ•จ์ˆ˜๋Š” async/await ์‚ฌ์šฉ ์‹œ ๋ช‡ ๊ฐ€์ง€ ๋ณ„๋‚œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ.

async function fn1() {
// SyntaxError: await p๋Š” async ํ•จ์ˆ˜ fn1์— ์žˆ์งˆ ์•Š์Šต๋‹ˆ๋‹ค
[1, 2].forEach(p => {
await p;
});
}
โ€‹
async function fn2() {
[Promise.resolve(1), Promise.resolve(2)].forEach(async (p) => {
console.log(await p);
});
โ€‹
// '1' & '2'์— ์•ž์„œ 'Done'์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด,
// ์œ„ await์€ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์ผ์‹œ์ •์ง€ ์‹œํ‚ต๋‹ˆ๋‹ค. fn2()๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค!
console.log('Done');
}

๋ฐฐ์—ด๊ณผ async ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ๊ฐ–๋Š” forEachAsync() ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.

๋ฐฐ์—ด์˜ ์š”์†Œ๋“ค์— ๋Œ€ํ•ด ์ˆœ์ฐจ์ ์œผ๋กœ fn()์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. forEachAsync()๋Š” ๋‹ค์Œ ์š”์†Œ๋ฅผ ์ด์–ด๊ฐ€๊ธฐ ์ „์— ํ˜„์žฌ ์š”์†Œ์˜ fn()์„ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ์ดˆ๊ธฐ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. node.js๋กœ ์ด๋ฒˆ ์˜ˆ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €๋กœ codepen ์‚ฌ์ดํŠธ(http://bit.ly/async-await-exercise-32)์— ์ ‘์†ํ•ด์„œ ์™„์„ฑํ•ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

// ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.
async function forEachAsync(arr, fn) {
throw Error("Not Implemented!");
}
โ€‹
// Below is test code, don't modify this
run().catch((err) => console.log(err.stack));
โ€‹
async function run() {
let i = 0;
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
โ€‹
async function fn1(n) {
await new Promise((resolve) => setTimeout(resolve, 100 - n * 10));
if (i++ !== n) throw Error("Make sure to `await` on `fn()`");
}
โ€‹
await forEachAsync(arr, fn1);
if (i !== 10) throw Error("Call `fn()` on every array element");
console.log("Success!");
}