[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
ํต์ฌ ๊ฐ๋ โ
- catch๋ ์๋ฌ๋ฅผ ์บก์ฒํ๊ณ ์ ์ ๊ฐ์ ๋ฐํ:
catch()๊ฐ ์ ์ ๊ฐ์ ๋ฐํํ๋ฉด, Promise ์ฒด์ธ์ ํ์.then()์ ๊ณ์ ์คํํฉ๋๋ค - catch ์ดํ์ then์ ๊ณ์ ์คํ: ์๋ฌ๊ฐ ์ด๋ฏธ ์ฒ๋ฆฌ๋์์ผ๋ฏ๋ก Promise ์ฒด์ธ์ ์ ์ ์ํ๋ก ๋ณต๊ทํฉ๋๋ค
- ๋ง์ง๋ง 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
}
์คํ ์์ ๋ถ์:
- ๋๊ธฐ ์ฝ๋:
console.log('Warrior')โWarrior์ถ๋ ฅ - Micro task:
Promise.resolve().then(a)โa()์คํ,Warlock์ถ๋ ฅ - Macro task:
setTimeout(b, 0)๋จผ์ ์คํ (0ms ์ง์ฐ)b()์คํ,Druid์ถ๋ ฅb()๋ด์Promise.resolve().then(...)์ micro task, ์ฆ์ ์คํ,Rogue์ถ๋ ฅ
- 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 โ 4 โ 7
- Micro task: 6
- Macro task (์ง์ฐ ์๊ฐ ์): 3 โ 2
๋ตโ
1
4
7
6
3
2
ํต์ฌ ๊ฐ๋ โ
- Promise ์์ฑ์ ๋ด์ ์ฝ๋๋ ๋๊ธฐ์ ์ผ๋ก ์คํ:
console.log(4)๋ ๋น๋๊ธฐ๊ฐ ์๋๋๋ค .then()๊ณผ.catch()๋ง ๋น๋๊ธฐ: micro task์ ์ํฉ๋๋ค- ์คํ ์์: ๋๊ธฐ ์ฝ๋ โ 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์ ๋น๋๊ธฐ ๋ฉ์ปค๋์ฆ ์ฌ์ธต ์ดํด