๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐Ÿค Chapter 7: Promise์™€ async/await ๊ตฌ๋ฌธ

๐Ÿฆ„ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜โ€‹

๐Ÿ“š ๋™๊ธฐ์™€ ๋น„๋™๊ธฐ APIโ€‹

  • Node.js์—์„œ ํŒŒ์ผ ์‹œ์Šคํ…œ๊ณผ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ์„ ๋ชจ์•„๋‘” fs ํŒจํ‚ค์ง€๋ฅผ ์ œ๊ณตํ•˜๋Š”๋ฐ, ๋™๊ธฐ ๋น„๋™๊ธฐ ๋ฒ„์ „์œผ๋กœ ๋‚˜๋ˆ„์–ด ์ œ๊ณตํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋™๊ธฐ ๋ฒ„์ „์ธ readFileSync์™€ ๋น„๋™๊ธฐ ๋ฒ„์ „์ธ readFile๋กœ ์ œ๊ณตํ•œ๋‹ค.
import { readFileSync, readFile } from "fs";

// ๋™๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์ฝ๊ธฐ
const buffer: Buffer = readFileSync('./package.json');
console.log(buffer.toString());

// ๋น„๋™๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์ฝ๊ธฐ
readFile('./package.json', (error: Error, buffer: Buffer) => {
console.log(buffer.toString());
});

// Promise์™€ async/await ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•œ ์˜ˆ
const readFilePromise = (filename: string): Promise<string> =>
new Promise<string>((resolve, reject) => {
readFile(filename, (error: Error, buffer: Buffer) => {
if(error) {
reject(error);
} else {
resolve(buffer.toString());
}
});
});

(async () => {
const content = await readFilePromise('./package.json');
console.log(content);
})();
  • API ํ•จ์ˆ˜๋Š” ์ผ๋ฐ˜ ํ•จ์ˆ˜์™€ ๋‹ฌ๋ฆฌ ํ•˜๋“œ๋””์Šคํฌ์— ์ €์žฅ๋œ ํŒŒ์ผ์„ ์ฝ๋Š” ๋“ฑ ์‹คํ–‰์‹œ ๋ฌผ๋ฆฌ์ ์ธ ์‹œ๊ฐ„์ด ์†Œ์š”๋œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ํŒŒ์ผ ๋‚ด์šฉ์„ ๋ชจ๋‘ ์ฝ์„ ๋•Œ๊นŒ์ง€ ํ”„๋กœ๊ทธ๋žจ์˜ ๋™์ž‘์„ ์ž ์‹œ ๋ฉˆ์ถ”๋Š” ๋™๊ธฐ ๋ฐฉ์‹์˜ API์™€ ํ”„๋กœ๊ทธ๋žจ์˜ ๋™์ž‘์„ ๋ฉˆ์ถ”์ง€ ์•Š๋Š” ๋Œ€์‹  ๊ฒฐ๊ณผ๋ฅผ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋กœ ์–ป๊ฒŒ ํ•˜๋Š” ๋น„๋™๊ธฐ ๋ฐฉ์‹์˜ API๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ๋น„๋™๊ธฐ API์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ผ๊ณ  ํ•œ๋‹ค. ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ์ผ๋ฐ˜ ํ•จ์ˆ˜์™€ ๋‹ฌ๋ฆฌ API์˜ ๋ฌผ๋ฆฌ์ ์ธ ๋™์ž‘ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๋ชฉ์ ์œผ๋กœ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค.

๐Ÿ“š ๋‹จ์ผ ์Šค๋ ˆ๋“œ์™€ ๋น„๋™๊ธฐ APIโ€‹

  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋ฏ€๋กœ ๋  ์ˆ˜ ์žˆ์œผ๋ฉด readFileSync์™€ ๊ฐ™์€ ๋™๊ธฐ API๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค.
  • ๋™๊ธฐ API๊ฐ€ ์‹คํ–‰๋˜๋ฉด, ์šด์˜์ฒด์ œ๋Š” ๋™๊ธฐ API์˜ ์ž‘์—… ๊ฒฐ๊ณผ๋ฅผ ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ๋Œ๋ ค์ค˜์•ผ ํ•œ๋‹ค. ์ด ๋•Œ๋ฌธ์— ์šด์˜์ฒด์ œ๋Š” ๋™๊ธฐ API๊ฐ€ ์‹คํ–‰๋œ ์ฝ”๋“œ๋ฅผ ์ผ์‹œ์ ์œผ๋กœ ๋ฉˆ์ถ˜ ๋‹ค์Œ, ๋˜ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์‹ค์ œ ์ž‘์—…์„ ์‹คํ–‰ํ•ด ์ผค๊ณผ๋ฅผ ์–ป์œผ๋ฉด ๊ทธ๋•Œ์„œ์•ผ ์ž ์‹œ ๋ฉˆ์ท„๋˜ ๋™๊ธฐ API๋ฅผ ์‹คํ–‰ํ•˜๋ฉด์„œ ๊ฒฐ๊ด๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ๊นŒ์ง€ ์ผ์‹œ์ ์œผ๋กœ ๋ฉˆ์ถ”๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•œ๋‹ค.

๐Ÿ“š ์ฝœ๋ฐฑ ์ง€์˜ฅโ€‹

  • ๋น„๋™๊ธฐ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—์„œ ๋˜ ๋‹ค์‹œ ๋‹ค๋ฅธ ๋น„๋™๊ธฐ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค ๋•Œ ์ฝ”๋“œ๊ฐ€ ๋งค์šฐ ๋ณต์žกํ•ด์ง„๋‹ค.
import { readFile } from "fs";

readFile('./package.json', (error: Error, buffer: Buffer) => {
if (error) {
throw error;
} else {
const content: string = buffer.toString();
console.log(content);
}

readFile('./tsconfig.json',(err: Error, buffer: Buffer) => {
if (error) {
throw error;
} else {
const content: string = buffer.toString();
console.log(content);
}
})
});
  • Promise๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ์ฝœ๋ฐฑ ์ง€์˜ฅ์— ๋น ์ง„ ์ฝ”๋“œ๋ฅผ ์ข€ ๋” ๋‹ค๋ฃจ๊ธฐ ์‰ฌ์šด ํ˜•ํƒœ์˜ ์ฝ”๋“œ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

๐Ÿฆ„ Promise ์ดํ•ดํ•˜๊ธฐโ€‹

  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ Promise๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ œ๋„ค๋ฆญ ํด๋ž˜์Šค ํ˜•ํƒœ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
const numPromise: Promise<number> = new Promise<number>(์ฝœ๋ฐฑํ•จ์ˆ˜);
const strPromise: Promise<string> = new Promise<string>(์ฝœ๋ฐฑํ•จ์ˆ˜);
const arrayPromise: Promise<number[]> = new Promise<number[]>(์ฝœ๋ฒกํž˜์ˆ˜);
  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ Promise์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ resolve์™€ reject ํ•จ์ˆ˜๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š” ํ˜•ํƒœ์ด๋‹ค.
new Promise<T>((
resolve: (successValue: T) => void,
reject: (any) => void,
) => {
// ์ฝ”๋“œ ๊ตฌํ˜„
});

๐Ÿ“š resolve์™€ reject ํ•จ์ˆ˜โ€‹

  • ๋‹ค์Œ์€ ๋น„๋™๊ธฐ API์ธ readFile์„ ํ˜ธ์ถœํ•˜๋Š” ๋‚ด์šฉ์„ ํ”„๋กœ๋ฏธ์Šค๋กœ ๊ตฌํ˜„ํ•œ ์˜ˆ์ด๋‹ค.
import { readFile } from 'fs';

export const readFilePromise = (filename: string): Promise<string> =>
new Promise<string>((
resolve: (value: string) => void,
reject: (error: Error) => void) => {
readFile(filename, (err: Error, buffer: Buffer) => {
if(err) {
reject(err);
} else {
resolve(buffer.toString());
}
})
}
)
  • ๋‹ค์Œ ์ฝ”๋“œ๋Š” readFilePromise ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” Promise ํƒ€์ž… ๊ฐ์ฒด์˜ then, catch, finally ๋ฉ”์„œ๋“œ๋ฅผ ๋ฉ”์„œ๋“œ ์ฒด์ธ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
import { readFilePromise } from "./readFilePromise";

readFilePromise('./package.json')
.then((content: string) => {
console.log(content);
return readFilePromise('./tsconfig.json');
})
.then((content: string) => {
console.log(content);
return readFilePromise('.');
})
.catch((err: Error) => console.log('error: ', err.message))
.finally(() => console.log('ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ'));

๐Ÿ“š Promise.resolve์™€ Promise.reject ๋ฉ”์„œ๋“œโ€‹

  • Promise.resolve(๊ฐ’) ํ˜•ํƒœ๋กœ ํ˜ธ์ถœํ•˜๋ฉด ํ•ญ์ƒ ์ด ๊ฐ‘์€ then ๋ฉ”์„œ๋“œ์—์„œ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
Promise.resolve({ name: 'Jack', age: 32 })
.then(value => console.log(value)); // { name: 'Jack', age: 32 }
  • Promise.reject(Error ํƒ€์ž… ๊ฐ์ฒด)๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ด Error ํƒ€์ž… ๊ฐ์ฒด๋Š” ํ•ญ์ƒ catch ๋ฉ”์„œ๋“œ์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—์„œ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
Promise.reject(new Error('์—๋Ÿฌ ๋ฐœ์ƒ'))
.catch((err: Error) => console.log('error: ', err.message)); // error: ์—๋Ÿฌ ๋ฐœ์ƒ

๐Ÿ“š then-์ฒด์ธโ€‹

  • Promise ๊ฐ์ฒด์— then ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ ํ˜•ํƒœ๋ฅผ then-์ฒด์ธ์ด๋ผ๊ณ  ํ•œ๋‹ค.
Promise.resolve(1)
.then((value: number) => {
console.log(value); // 1
return Promise.resolve(true);
})
.then((value: boolean) => {
console.log(value); // true
return [1, 2, 3];
})
.then((value: number[]) => {
console.log(value); // [1, 2, 3]
return { name: 'jack', age: 32 };
})
.then((value: { name: string, age: number }) => {
console.log(value); // { name: 'jack', age: 32 }
})

๐Ÿ“šPromise.all ๋ฉ”์„œ๋“œโ€‹

  • Promise.all ๋ฉ”์„œ๋“œ๋Š” Promise ๊ฐ์ฒด๋ฅผ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ›์•„, ๋ชจ๋“  ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ resolve๋œ ๊ฐ’๋“ค์˜ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.
  • Promise.all ๋ฉ”์„œ๋“œ๋Š” ์ด๋Ÿฐ ๋‚ด์šฉ์œผ๋กœ ๊ตฌ์„ฑ๋œ ๋˜ ๋‹ค๋ฅธ Promise ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ํ•ด์†Œ๋œ ๊ฐ’๋“ค์˜ ๋ฐฐ์—ด์€ then ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์–ป๋Š”๋‹ค.
  • ๋งŒ์•ฝ, ๋ฐฐ์—ด์— ๋‹ด๊ธด Promise ๊ฐ์ฒด ์ค‘ ๊ฑฐ์ ˆ ๊ฐ์ฒด๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋” ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ํ•ด๋‹น ๊ฑฐ์ ˆ ๊ฐ’์„ ๋‹ด์€ Promise.reject ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
const getAllResolvedResult = <T>(promises: Promise<T>[]) => Promise.all(promises);

getAllResolvedResult<any>([Promise.resolve(true), Promise.resolve('hello')])
.then(result => console.log(result)); // [true, 'hello']

getAllResolvedResult<any>([Promise.reject(new Error('error')), Promise.resolve(1)])
.then(result => console.log(result)) // ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.
.catch(error => console.log('error: ', error.message)); // error: error

๐Ÿ“š Promise.race ๋ฉ”์„œ๋“œโ€‹

  • Promise.raceํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋Š” ๋ฐฐ์—ด์— ๋‹ด๊ธด ํ”„๋กœ๋ฏธ์Šค ๊ฐ์ฒด ์ค‘ ํ•˜๋‚˜๋ผ๋„ resolve๋˜๋ฉด ์ด ๊ฐ’์„ ๋‹ด์€ Promise.resolve ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋งŒ์ผ ๊ฑฐ์ ˆ ๊ฐ’์ด ๊ฐ€์žฅ ๋จผ์ € ๋ฐœ์ƒํ•˜๋ฉด promise.reject ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
Promise.race([Promise.resolve(true), Promise.resolve('hello')])
.then(value => console.log(value)); // true

Promise.race([Promise.resolve(true), Promise.reject(new Error('hello'))])
.then(value => console.log(value)) // true
.catch(error => console.log(error.message)); // ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค

Promise.race([Promise.reject(new Error('error')), Promise.resolve(true)])
.then(value => console.log(value)) // ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค
.catch(error => console.log(error.message)); // error

๐Ÿฆ„ async์™€ await ๊ตฌ๋ฌธโ€‹

๐Ÿ“š async ํ•จ์ˆ˜์˜ ๋‘ ๊ฐ€์ง€ ์„ฑ์งˆโ€‹

  • async ํ•จ์ˆ˜ ์ˆ˜์ •์ž๊ฐ€ ๋ถ™์€ ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„ฑ์งˆ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
  1. ์ผ๋ฐ˜ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. Promise ๊ฐ์ฒด๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“š async ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์˜ ์˜๋ฏธโ€‹

  • async ํ•จ์ˆ˜๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ๋ฐ˜ํ™˜๊ฐ’์€ Promise ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜๋˜๋ฏ€๋กœ ๋‹ค์Œ์ฒ˜๋Ÿผ then ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด async ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ์–ป์–ด์•ผ ํ•œ๋‹ค.
const asyncReturn = async() => {
return [1, 2, 3];
}

asyncReturn()
.then(value => console.log(value)); // [1, 2, 3]

๐Ÿ“š async ํ•จ์ˆ˜์˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌโ€‹

const asyncException = async () => {
throw new Error('error');
}

asyncException()
.catch(err => console.log('error: ', err.message)); // error: error

๐Ÿ“š async ํ•จ์ˆ˜์™€ Promise.allโ€‹

import { readFilePromise } from "./readFilePromise"

const readFilesAll = async (fileNames: string[]) => {
return await Promise.all(
fileNames.map(fileNames => readFilePromise(fileNames))
);
}

readFilesAll(['./package.json', './tsconfig.json'])
.then(([packageJson, tsConfigJson]: string[]) => {
console.log(packageJson);
console.log(tsConfigJson);
})
.catch(err => console.log(err))