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

[Medium] ๐Ÿ“„ Promise

Promise๋ž€ ๋ฌด์—‡์ธ๊ฐ€?โ€‹

Promise๋Š” ES6์˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์œผ๋กœ, ์ฃผ๋กœ callback hell ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ๋” ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. Promise๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์ตœ์ข… ์™„๋ฃŒ ๋˜๋Š” ์‹คํŒจ, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๊ฒฐ๊ณผ๊ฐ’์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

Promise์—๋Š” ์„ธ ๊ฐ€์ง€ ์ƒํƒœ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • pending(์ง„ํ–‰ ์ค‘): ์ดˆ๊ธฐ ์ƒํƒœ
  • fulfilled(์™„๋ฃŒ๋จ): ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋จ
  • rejected(๊ฑฐ๋ถ€๋จ): ์ž‘์—… ์‹คํŒจ

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•โ€‹

Promise ์ƒ์„ฑโ€‹

const myPromise = new Promise((resolve, reject) => {
// ๋น„๋™๊ธฐ ์ž‘์—…
const success = true;

if (success) {
resolve('์„ฑ๊ณต!'); // Promise ์ƒํƒœ๋ฅผ fulfilled๋กœ ๋ณ€๊ฒฝ
} else {
reject('์‹คํŒจ!'); // Promise ์ƒํƒœ๋ฅผ rejected๋กœ ๋ณ€๊ฒฝ
}
});

myPromise
.then((result) => {
console.log(result); // '์„ฑ๊ณต!'
})
.catch((error) => {
console.log(error); // '์‹คํŒจ!'
});

์‹ค์ œ ์‘์šฉ: API ์š”์ฒญ ์ฒ˜๋ฆฌโ€‹

// api ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณต์šฉ function ์ƒ์„ฑ
function fetchData(url) {
return fetch(url)
.then((response) => {
// response๊ฐ€ 200 ~ 299 ๋ฒ”์œ„์— ์žˆ๋Š”์ง€ ํ™•์ธ
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json(); // response๋ฅผ json์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜
})
.catch((error) => {
// ๋„คํŠธ์›Œํฌ ์ด์ƒ ์—ฌ๋ถ€ ๋˜๋Š” ์š”์ฒญ ๊ฑฐ๋ถ€ ํ™•์ธ
console.log('There has been a problem with your fetch operation:', error);
throw error; // ์—๋Ÿฌ ๋˜์ง€๊ธฐ
});
}

fetchData('https://jsonplaceholder.typicode.com/users/1')
.then((userData) => {
console.log('User data received:', userData);
})
.catch((error) => {
console.log('Error:', error.message);
});

Promise์˜ ๋ฉ”์„œ๋“œโ€‹

.then() / .catch() / .finally()โ€‹

promise
.then((result) => {
// ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ ์ฒ˜๋ฆฌ
return result;
})
.catch((error) => {
// ์—๋Ÿฌ ์ฒ˜๋ฆฌ
console.error(error);
})
.finally(() => {
// ์„ฑ๊ณต์ด๋“  ์‹คํŒจ๋“  ํ•ญ์ƒ ์‹คํ–‰
console.log('Promise ์™„๋ฃŒ');
});

Promise.all()โ€‹

๋ชจ๋“  Promise๊ฐ€ ์™„๋ฃŒ๋˜์–ด์•ผ ์™„๋ฃŒ๋˜๋ฉฐ, ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜๋ฉด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve('foo'), 100)
);
const promise3 = Promise.resolve(42);

Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values); // [3, 'foo', 42]
});

์‚ฌ์šฉ ์‹œ๊ธฐ: ์—ฌ๋Ÿฌ API ์š”์ฒญ์ด ๋ชจ๋‘ ์™„๋ฃŒ๋œ ํ›„์— ๊ณ„์† ์‹คํ–‰ํ•ด์•ผ ํ•  ๋•Œ.

Promise.race()โ€‹

์ฒซ ๋ฒˆ์งธ๋กœ ์™„๋ฃŒ๋œ(์„ฑ๊ณต์ด๋“  ์‹คํŒจ๋“ ) Promise์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

const promise1 = new Promise((resolve) =>
setTimeout(() => resolve('1๋ฒˆ'), 500)
);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve('2๋ฒˆ'), 100)
);

Promise.race([promise1, promise2]).then((value) => {
console.log(value); // '2๋ฒˆ' (๋” ๋นจ๋ฆฌ ์™„๋ฃŒ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ)
});

์‚ฌ์šฉ ์‹œ๊ธฐ: ์š”์ฒญ ํƒ€์ž„์•„์›ƒ ์„ค์ •, ๊ฐ€์žฅ ๋น ๋ฅธ ์‘๋‹ต ๊ฒฐ๊ณผ๋งŒ ๊ฐ€์ ธ์˜ฌ ๋•Œ.

Promise.allSettled()โ€‹

๋ชจ๋“  Promise๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€(์„ฑ๊ณต์ด๋“  ์‹คํŒจ๋“ ) ๊ธฐ๋‹ค๋ฆฌ๊ณ , ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

const promise1 = Promise.resolve(3);
const promise2 = Promise.reject('์—๋Ÿฌ');
const promise3 = Promise.resolve(42);

Promise.allSettled([promise1, promise2, promise3]).then((results) => {
console.log(results);
// [
// { status: 'fulfilled', value: 3 },
// { status: 'rejected', reason: '์—๋Ÿฌ' },
// { status: 'fulfilled', value: 42 }
// ]
});

์‚ฌ์šฉ ์‹œ๊ธฐ: ๋ชจ๋“  Promise์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ์•Œ์•„์•ผ ํ•˜๋ฉฐ, ์ผ๋ถ€๊ฐ€ ์‹คํŒจํ•ด๋„ ๊ณ„์† ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋•Œ.

Promise.any()โ€‹

์ฒซ ๋ฒˆ์งธ๋กœ ์„ฑ๊ณตํ•œ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๋ชจ๋‘ ์‹คํŒจํ•ด์•ผ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

const promise1 = Promise.reject('์—๋Ÿฌ 1');
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve('์„ฑ๊ณต'), 100)
);
const promise3 = Promise.reject('์—๋Ÿฌ 2');

Promise.any([promise1, promise2, promise3]).then((value) => {
console.log(value); // '์„ฑ๊ณต'
});

์‚ฌ์šฉ ์‹œ๊ธฐ: ์—ฌ๋Ÿฌ ๋Œ€์ฒด ๋ฆฌ์†Œ์Šค๊ฐ€ ์žˆ๊ณ , ํ•˜๋‚˜๋งŒ ์„ฑ๊ณตํ•˜๋ฉด ๋˜๋Š” ๊ฒฝ์šฐ.

๋ฉด์ ‘ ๋ฌธ์ œโ€‹

๋ฌธ์ œ 1: Promise ์ฒด์ด๋‹๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌโ€‹

๋‹ค์Œ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํŒ๋‹จํ•ด ๋ณด์„ธ์š”:

Promise.resolve(1)
.then((x) => x + 1)
.then(() => {
throw new Error('My Error');
})
.catch((e) => 1)
.then((x) => x + 1)
.then((x) => console.log(x))
.catch((e) => console.log('This will not run'));

ํ•ด์„คโ€‹

์‹คํ–‰ ๊ณผ์ •์„ ๋‹จ๊ณ„๋ณ„๋กœ ๋ถ„์„ํ•ด ๋ด…์‹œ๋‹ค:

Promise.resolve(1) // ๋ฐ˜ํ™˜๊ฐ’: 1
.then((x) => x + 1) // x = 1, 2 ๋ฐ˜ํ™˜
.then(() => {
throw new Error('My Error'); // ์—๋Ÿฌ ๋ฐœ์ƒ, catch๋กœ ์ด๋™
})
.catch((e) => 1) // ์—๋Ÿฌ ์บก์ฒ˜, 1 ๋ฐ˜ํ™˜ (์ค‘์š”: ์—ฌ๊ธฐ์„œ ์ •์ƒ ๊ฐ’์„ ๋ฐ˜ํ™˜)
.then((x) => x + 1) // x = 1, 2 ๋ฐ˜ํ™˜
.then((x) => console.log(x)) // 2 ์ถœ๋ ฅ
.catch((e) => console.log('This will not run')); // ์‹คํ–‰๋˜์ง€ ์•Š์Œ

๋‹ต: 2

ํ•ต์‹ฌ ๊ฐœ๋…โ€‹

  1. catch๋Š” ์—๋Ÿฌ๋ฅผ ์บก์ฒ˜ํ•˜๊ณ  ์ •์ƒ ๊ฐ’์„ ๋ฐ˜ํ™˜: catch()๊ฐ€ ์ •์ƒ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด, Promise ์ฒด์ธ์€ ํ›„์† .then()์„ ๊ณ„์† ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค
  2. catch ์ดํ›„์˜ then์€ ๊ณ„์† ์‹คํ–‰: ์—๋Ÿฌ๊ฐ€ ์ด๋ฏธ ์ฒ˜๋ฆฌ๋˜์—ˆ์œผ๋ฏ€๋กœ Promise ์ฒด์ธ์€ ์ •์ƒ ์ƒํƒœ๋กœ ๋ณต๊ท€ํ•ฉ๋‹ˆ๋‹ค
  3. ๋งˆ์ง€๋ง‰ catch๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Œ: ์ƒˆ๋กœ์šด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค

์—๋Ÿฌ๋ฅผ ๊ณ„์† ์ „๋‹ฌํ•˜๋ ค๋ฉด catch์—์„œ ์—๋Ÿฌ๋ฅผ ๋‹ค์‹œ ๋˜์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค:

Promise.resolve(1)
.then((x) => x + 1)
.then(() => {
throw new Error('My Error');
})
.catch((e) => {
console.log('์—๋Ÿฌ ์บก์ฒ˜๋จ');
throw e; // ์—๋Ÿฌ๋ฅผ ๋‹ค์‹œ ๋˜์ง
})
.then((x) => x + 1) // ์‹คํ–‰๋˜์ง€ ์•Š์Œ
.then((x) => console.log(x)) // ์‹คํ–‰๋˜์ง€ ์•Š์Œ
.catch((e) => console.log('This will run')); // ์‹คํ–‰๋จ

๋ฌธ์ œ 2: Event Loop์™€ ์‹คํ–‰ ์ˆœ์„œโ€‹

์ด ๋ฌธ์ œ๋Š” Event Loop ๊ฐœ๋…์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค

๋‹ค์Œ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํŒ๋‹จํ•ด ๋ณด์„ธ์š”:

function a() {
console.log('Warlock');
}

function b() {
console.log('Druid');
Promise.resolve().then(() => {
console.log('Rogue');
});
}

function c() {
console.log('Mage');
}

function d() {
setTimeout(c, 100);
const temp = Promise.resolve().then(a);
console.log('Warrior');
setTimeout(b, 0);
}

d();

์‹คํ–‰ ์ˆœ์„œ ์ดํ•ดโ€‹

๋จผ์ € d()๋ฅผ ๋ด…์‹œ๋‹ค:

function d() {
setTimeout(c, 100); // 4. Macro task, 100ms ์ง€์—ฐ, ๋งˆ์ง€๋ง‰์— ์‹คํ–‰
const temp = Promise.resolve().then(a); // 2. Micro task, ๋™๊ธฐ ์ฝ”๋“œ ์™„๋ฃŒ ํ›„ ์‹คํ–‰
console.log('Warrior'); // 1. ๋™๊ธฐ ์‹คํ–‰, ์ฆ‰์‹œ ์ถœ๋ ฅ
setTimeout(b, 0); // 3. Macro task, 0ms ์ง€์—ฐ, ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ macro task
}

์‹คํ–‰ ์ˆœ์„œ ๋ถ„์„:

  1. ๋™๊ธฐ ์ฝ”๋“œ: console.log('Warrior') โ†’ Warrior ์ถœ๋ ฅ
  2. Micro task: Promise.resolve().then(a) โ†’ a() ์‹คํ–‰, Warlock ์ถœ๋ ฅ
  3. Macro task:
    • setTimeout(b, 0) ๋จผ์ € ์‹คํ–‰ (0ms ์ง€์—ฐ)
    • b() ์‹คํ–‰, Druid ์ถœ๋ ฅ
    • b() ๋‚ด์˜ Promise.resolve().then(...)์€ micro task, ์ฆ‰์‹œ ์‹คํ–‰, Rogue ์ถœ๋ ฅ
  4. Macro task: setTimeout(c, 100) ๋งˆ์ง€๋ง‰ ์‹คํ–‰ (100ms ์ง€์—ฐ), Mage ์ถœ๋ ฅ

๋‹ตโ€‹

Warrior
Warlock
Druid
Rogue
Mage

ํ•ต์‹ฌ ๊ฐœ๋…โ€‹

  • ๋™๊ธฐ ์ฝ”๋“œ > Micro task (Promise) > Macro task (setTimeout)
  • Promise์˜ .then()์€ micro task์— ์†ํ•˜๋ฉฐ, ํ˜„์žฌ macro task๊ฐ€ ๋๋‚œ ํ›„, ๋‹ค์Œ macro task๊ฐ€ ์‹œ์ž‘๋˜๊ธฐ ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค
  • setTimeout์€ ์ง€์—ฐ ์‹œ๊ฐ„์ด 0์ด์–ด๋„ macro task์— ์†ํ•˜๋ฉฐ, ๋ชจ๋“  micro task ์ดํ›„์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค

๋ฌธ์ œ 3: Promise ์ƒ์„ฑ์ž์˜ ๋™๊ธฐ์™€ ๋น„๋™๊ธฐโ€‹

๋‹ค์Œ ์ฝ”๋“œ์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํŒ๋‹จํ•ด ๋ณด์„ธ์š”:

function printing() {
console.log(1);
setTimeout(function () {
console.log(2);
}, 1000);
setTimeout(function () {
console.log(3);
}, 0);

new Promise((resolve, reject) => {
console.log(4);
resolve(5);
}).then((foo) => {
console.log(6);
});

console.log(7);
}

printing();

// output ?

Promise ๋ธ”๋ก์— ์ฃผ์˜โ€‹

์ด ๋ฌธ์ œ์˜ ํ•ต์‹ฌ์€: Promise ์ƒ์„ฑ์ž ๋‚ด์˜ ์ฝ”๋“œ๋Š” ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. .then()๊ณผ .catch()๋งŒ ๋น„๋™๊ธฐ์ž…๋‹ˆ๋‹ค.

์‹คํ–‰ ์ˆœ์„œ ๋ถ„์„:

console.log(1); // 1. ๋™๊ธฐ, 1 ์ถœ๋ ฅ
setTimeout(() => console.log(2), 1000); // 5. Macro task, 1000ms ์ง€์—ฐ
setTimeout(() => console.log(3), 0); // 4. Macro task, 0ms ์ง€์—ฐ

new Promise((resolve, reject) => {
console.log(4); // 2. ๋™๊ธฐ! Promise ์ƒ์„ฑ์ž ๋‚ด๋ถ€๋Š” ๋™๊ธฐ์ , 4 ์ถœ๋ ฅ
resolve(5);
}).then((foo) => {
console.log(6); // 3. Micro task, 6 ์ถœ๋ ฅ
});

console.log(7); // 3. ๋™๊ธฐ, 7 ์ถœ๋ ฅ

์‹คํ–‰ ํ๋ฆ„:

  1. ๋™๊ธฐ ์‹คํ–‰: 1 โ†’ 4 โ†’ 7
  2. Micro task: 6
  3. Macro task (์ง€์—ฐ ์‹œ๊ฐ„ ์ˆœ): 3 โ†’ 2

๋‹ตโ€‹

1
4
7
6
3
2

ํ•ต์‹ฌ ๊ฐœ๋…โ€‹

  1. Promise ์ƒ์„ฑ์ž ๋‚ด์˜ ์ฝ”๋“œ๋Š” ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰: console.log(4)๋Š” ๋น„๋™๊ธฐ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค
  2. .then()๊ณผ .catch()๋งŒ ๋น„๋™๊ธฐ: micro task์— ์†ํ•ฉ๋‹ˆ๋‹ค
  3. ์‹คํ–‰ ์ˆœ์„œ: ๋™๊ธฐ ์ฝ”๋“œ โ†’ micro task โ†’ macro task

ํ”ํ•œ ์‹ค์ˆ˜โ€‹

1. return ์žŠ๊ธฐโ€‹

Promise ์ฒด์ธ์—์„œ return์„ ์žŠ์œผ๋ฉด ํ›„์† .then()์ด undefined๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

// โŒ ์ž˜๋ชป๋œ ๋ฐฉ๋ฒ•
fetchUser()
.then((user) => {
fetchPosts(user.id); // return ์žŠ์Œ
})
.then((posts) => {
console.log(posts); // undefined
});

// โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•
fetchUser()
.then((user) => {
return fetchPosts(user.id); // return ๊ธฐ์–ต
})
.then((posts) => {
console.log(posts); // ์˜ฌ๋ฐ”๋ฅธ ๋ฐ์ดํ„ฐ
});

2. catch๋กœ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ์žŠ๊ธฐโ€‹

์บก์ฒ˜๋˜์ง€ ์•Š์€ Promise ์—๋Ÿฌ๋Š” UnhandledPromiseRejection์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค:

// โŒ ์บก์ฒ˜๋˜์ง€ ์•Š๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
fetchData()
.then((data) => {
return processData(data);
})
.then((result) => {
console.log(result);
});

// โœ… catch ์ถ”๊ฐ€
fetchData()
.then((data) => {
return processData(data);
})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error('์—๋Ÿฌ ๋ฐœ์ƒ:', error);
});

3. Promise ์ƒ์„ฑ์ž ๋‚จ์šฉโ€‹

์ด๋ฏธ Promise์ธ ํ•จ์ˆ˜๋ฅผ Promise๋กœ ๋‹ค์‹œ ๊ฐ์Œ€ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค:

// โŒ ๋ถˆํ•„์š”ํ•œ ๋ž˜ํ•‘
function fetchData() {
return new Promise((resolve, reject) => {
fetch(url)
.then((response) => resolve(response))
.catch((error) => reject(error));
});
}

// โœ… ์ง์ ‘ ๋ฐ˜ํ™˜
function fetchData() {
return fetch(url);
}

4. ์—ฌ๋Ÿฌ catch ์—ฐ๊ฒฐโ€‹

๊ฐ catch()๋Š” ๊ทธ ์ด์ „์˜ ์—๋Ÿฌ๋งŒ ์บก์ฒ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

Promise.resolve()
.then(() => {
throw new Error('Error 1');
})
.catch((e) => {
console.log('์บก์ฒ˜๋จ:', e.message); // ์บก์ฒ˜๋จ: Error 1
})
.then(() => {
throw new Error('Error 2');
})
.catch((e) => {
console.log('์บก์ฒ˜๋จ:', e.message); // ์บก์ฒ˜๋จ: Error 2
});

๊ด€๋ จ ์ฃผ์ œโ€‹

  • async/await - ๋” ์šฐ์•„ํ•œ Promise ๋ฌธ๋ฒ•์  ์„คํƒ•
  • Event Loop - JavaScript์˜ ๋น„๋™๊ธฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์‹ฌ์ธต ์ดํ•ด

Referenceโ€‹