JS Promise
Table of contents
JavaScript Promise
ํ๋ก๋ฏธ์ค๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ์ฌ์ฉ๋๋ ๊ฐ์ฒด์ด๊ณ ์ฃผ๋ก ์๋ฒ์์ ๋ฐ์์จ ๋ฐ์ดํฐ๋ฅผ ํ๋ฉด์ ํ์ํ ๋ ์ฌ์ฉํ๋ค.
ES6์์๋ ์ ํต์ ์ธ ์ฝ๋ฐฑ ํจํด์ด ๊ฐ์ง ๋จ์ ์ ๋ณด์ํ๊ณ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์์ ์ ๋ช ํํ๊ฒ ํํํ๋ ํจํด์ผ๋ก Promise๋ฅผ ๋์ ํ๋ค.
Promise์ States
new Promise()
๋ก ํ๋ก๋ฏธ์ค๋ฅผ ์์ฑํ๊ณ ์ข
๋ฃ ๋ ๋๊น์ง ์ํ๋ฅผ ๊ฐ๋๋ค. (ํ๋ก๋ฏธ์ค์ ์ฒ๋ฆฌ ๊ณผ์ )
์ํ | ์๋ฏธ | ๊ตฌํ |
---|---|---|
Pending | (๋๊ธฐ) ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์์ง ์ํ๋์ง ์์ ์ํ | resolve ๋๋ reject ํจ์๊ฐ ์์ง ํธ์ถ๋์ง ์์ ์ํ |
Fulfilled | (์ฑ๊ณต) ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์๋ฃ๋์ด ํ๋ก๋ฏธ์ค๊ฐ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐํํด์ค ์ํ | resolve ํจ์๊ฐ ํธ์ถ๋ ์ํ |
Rejected | (์คํจ) ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์คํจํ๊ฑฐ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ํ | reject ํจ์๊ฐ ํธ์ถ๋ ์ํ |
Settled | fulfilled ๋๋ rejected์ ์๊ด ์์ด pending ์ด ์๋ ์ํ, ์ฆ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์ํ๋ ์ํ | resolve ๋๋ reject ํจ์๊ฐ ํธ์ถ๋ ์ํ |
Pending(๋๊ธฐ)
new Promise()
๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด pending ์ํ๊ฐ ๋๋ค.
new Promise();
new Promise()
๋ฉ์๋๋ฅผ ํธ์ถํ ๋ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ ์ธํ ์ ์๊ณ , ์ฝ๋ฐฑ ํจ์์ ์ธ์๋ resolve
, reject
ํจ์์ด๋ค.
new Promise(function(resolve, reject) {// ...});
- Promise ์์ฑ์ ํจ์์ ์ธ์: ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํํ ์ฝ๋ฐฑ ํจ์
- ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํํ ์ฝ๋ฐฑ ํจ์์ ์ธ์:
resolve
ํจ์,reject
ํจ์
Fulfilled(์ดํ)
์ฝ๋ฐฑ ํจ์์ ์ธ์ resolve
๋ฅผ ์๋์ ๊ฐ์ด ์คํํ๋ฉด fulfilled ์ํ๊ฐ ๋๋ค.
new Promise(function(resolve, reject) {resolve();});
์ดํ ์ํ๊ฐ ๋๋ฉด ์๋์ ๊ฐ์ด then()
์ ์ด์ฉํ์ฌ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐ์ ์ ์๋ค.
function getData() {return new Promise(function(resolve, reject) {var data = 100;resolve(data);});}// resolve()์ ๊ฒฐ๊ณผ ๊ฐ data๋ฅผ resolvedData๋ก ๋ฐ์getData().then(function(resolvedData) {console.log(resolvedData); // 100});
Rejected(์คํจ)
์ฝ๋ฐฑ ํจ์์ ์ธ์ reject
๋ฅผ ์๋์ ๊ฐ์ด ํธ์ถํ๋ฉด rejected ์ํ๊ฐ ๋๋ค.
new Promise(function(resolve, reject) {reject();});
์คํจ ์ํ๊ฐ ๋๋ฉด ์คํจ ์ฒ๋ฆฌ์ ๊ฒฐ๊ณผ ๊ฐ(์๋ฌ)์ catch()
๋ก ๋ฐ์ ์ ์๋ค.
function getData() {return new Promise(function(resolve, reject) {reject(new Error("Request is failed"));});}// reject()์ ๊ฒฐ๊ณผ ๊ฐ Error๋ฅผ err์ ๋ฐ์getData().then().catch(function(err) {console.log(err); // Error: Request is failed});
Promise ์ฒ๋ฆฌ ๊ณผ์
์ถ์ฒ: ์๋ฐ์คํฌ๋ฆฝํธ Promise ์ฝ๊ฒ ์ดํดํ๊ธฐ โข Captain Pangyo
Promise ์์ฑ์ ํจ์๋ฅผ ํตํด ์ธ์คํด์คํ
- ์ฝ๋ฐฑ ํจ์๋ฅผ ์ธ์๋ก ์ ๋ฌ๋ฐ๋๋ฐ ์ด ์ฝ๋ฐฑ ํจ์๋
resolve
์reject
ํจ์๋ฅผ ์ธ์๋ก ์ ๋ฌ
- ์ฝ๋ฐฑ ํจ์๋ฅผ ์ธ์๋ก ์ ๋ฌ๋ฐ๋๋ฐ ์ด ์ฝ๋ฐฑ ํจ์๋
Promise ์์ฑ์ ํจ์๊ฐ ์ธ์๋ก ์ ๋ฌ๋ฐ์ ์ฝ๋ฐฑ ํจ์ ๋ด๋ถ์์ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ํ
ํ๋ก๋ฏธ์ค๋ก ๊ตฌํ๋ ๋น๋๊ธฐ ํจ์ ๋ด๋ถ์์ Promise ๊ฐ์ฒด๋ฅผ ์์ฑ
๋น๋๊ธฐ์ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ
resolve
์reject
ํจ์์ ์ธ์๋ก ์ ๋ฌํ๋ฉด์ ํธ์ถ- ์ฑ๊ณต:
resolve
ํจ์์ ๊ฒฐ๊ณผ ๊ฐ ์ ๋ฌ, ํธ์ถ - ์คํจ:
reject
ํจ์์ ๊ฒฐ๊ณผ ๊ฐ ์ ๋ฌ, ํธ์ถ
- ์ฑ๊ณต:
ํ๋ก๋ฏธ์ค๋ก ๊ตฌํ๋ ๋น๋๊ธฐ ํจ์๋ Promise ๊ฐ์ฒด๋ฅผ ๋ฐํ
๋ฐํ๋ Promise ๊ฐ์ฒด๋ ์ํ(state) ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๊ณ , ํธ์ถํ ํจ์์ ๋ฐ๋ผ์ ๊ฒฐ์ ๋๋ค.
resolve
ํจ์ ํธ์ถ: fulfilled (์ฑ๊ณต)reject
ํจ์ ํธ์ถ: rejected (์คํจ)
๋น๋๊ธฐ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ ๊ฐ์ Promise ๊ฐ์ฒด์ ํ์ ์ฒ๋ฆฌ ๋ฉ์๋์ ์ ๋ฌ
Promise ํ์ ์ฒ๋ฆฌ ๋ฉ์๋
ํ๋ก๋ฏธ์ค๋ก ๊ตฌํ๋ ๋น๋๊ธฐ ํจ์๋ Promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ์ฌ์ผ ํ๋ค.
ํ๋ก๋ฏธ์ค๋ก ๊ตฌํ๋ ๋น๋๊ธฐ ํจ์๋ฅผ ํธ์ถํ๋ ์ธก(promise consumer)์์๋ Promise ๊ฐ์ฒด์ ํ์ ์ฒ๋ฆฌ ๋ฉ์๋ then, catch, finally๋ฅผ ํตํด ๋น๋๊ธฐ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ ๋๋ ์๋ฌ ๋ฉ์์ง๋ฅผ ์ ๋ฌ๋ฐ์ ํ์ ์ฒ๋ฆฌ๋ฅผ ์ํํ๋ค.
Promise ๊ฐ์ฒด๋ ์ํ๋ฅผ ๊ฐ๋๋ค๊ณ ํ์๋ค.
์ด ์ํ์ ๋ฐ๋ผ ํ์ ์ฒ๋ฆฌ ๋ฉ์๋๋ฅผ ์ฒด์ด๋ ๋ฐฉ์์ผ๋ก ํธ์ถํ๋ค. ํ๋ก๋ฏธ์ค์ ํ์ ์ฒ๋ฆฌ ๋ฉ์๋๋ ์๋์ ๊ฐ๋ค.
Promise.prototype.then()
.then()
์ ๋ ๊ฐ์ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ธ์๋ก ์ ๋ฌ ๋ฐ๊ณ Promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
- ์ฒซ ๋ฒ์งธ ์ฝ๋ฐฑ ํจ์๋ ํ๋ก๋ฏธ์ค์ ์ฑ๊ณต(fulfilled, resolve ํจ์๊ฐ ํธ์ถ๋ ์ํ) ์ ํธ์ถ
- ๋ ๋ฒ์งธ ์ฝ๋ฐฑ ํจ์๋ ํ๋ก๋ฏธ์ค์ ์คํจ(rejected, reject ํจ์๊ฐ ํธ์ถ๋ ์ํ) ์ ํธ์ถ
promise.then(function(result) {// ...},function(error) {// ...});// ES6 ํ์ดํ ํจ์ ๋ฌธ๋ฒpromise.then(result => { /* ... */},error => { /* ... */});
.then(null, f)
์๋ฌ๋ง ๋ค๋ฃจ๊ณ ์ถ๋ค๋ฉด ์ฒซ ๋ฒ์งธ ์ธ์๋ก
null
์ ์ ๋ฌํ๋ฉด ๋๋ค.
Promise.prototype.catch()
.catch()
๋ ํ ๊ฐ์ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ธ์๋ก ์ ๋ฌ ๋ฐ๊ณ Promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. ์ฃผ๋ก ์๋ฌ๋ฅผ ํธ๋ค๋ง ํ ๋ ์ฌ์ฉ๋๋ค.
- ํ๋ก๋ฏธ์ค์ ์์ธ(๋น๋๊ธฐ ์ฒ๋ฆฌ ์๋ฌ, then์์ ๋ฐ์ํ ์๋ฌ) ๋ฐ์์ ํธ์ถ
promise.catch(function(error) {// ...});// ES6 ํ์ดํ ํจ์ ๋ฌธ๋ฒpromise.catch(error => { /* ... */ });
.catch(f)
==.then(null,f)
์์ ๋ ๊ฐ๊ฐ ๋์ผํ๋ค๊ณ ํ๋ ๊ฒ์ ๋ณด๋ syntax sugar์ธ ๋ฏ ํ๋ค.
Promise.prototype.finally()
.finally()
์ธ์๊ฐ ์๊ณ ํ๋ก๋ฏธ์ค์ ์ํ์ ์๊ด์์ด ์ํํด์ผ ํ ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
.then()
๊ณผ .catch()
๋ฉ์๋์์ ๊ณตํต์ ์ผ๋ก ์ํํด์ผํ ๊ฒ์ ์ฒ๋ฆฌํ์ฌ ์ค๋ณต์ ํผํ ์ ์๋ค.
.finally()
๋ฉ์๋๋ ํ๋ผ๋ฏธ์ค ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๋ง๋ค์ด์ง ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ finally๋ฅผ ํต๊ณผํ์ฌ ํ์ ๋ฉ์๋์ ์ ๋ฌ ๋๋ค.
- ํ๋ก๋ฏธ์ค๊ฐ ์ฒ๋ฆฌ๋๋ฉด ์ฑ๊ณต(fulfilled) ๋๋ ์คํจ(rejected)์ ์๊ด์์ด ํธ์ถ
promise.finally(function() {// ...});// ES6 ํ์ดํ ํจ์ ๋ฌธ๋ฒpromise.finally(() => { /* ... */ });
TC39 Stage 4
Expected Publication Year: 2018์ฐธ์กฐ: Promise.prototype.finally() - JavaScript | MDN / tc39/proposal-promise-finally
Promise Chaining
ํ๋ก๋ฏธ์ค๋ ํ์ ์ฒ๋ฆฌ ๋ฉ์๋๋ฅผ ์ฒด์ด๋(chaining)ํ์ฌ ์ฌ๋ฌ ๊ฐ์ ํ๋ก๋ฏธ์ค๋ฅผ ์ฐ๊ฒฐํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
Promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ ๋น๋๊ธฐ ํจ์๋ ํ๋ก๋ฏธ์ค ํ์ ์ฒ๋ฆฌ ๋ฉ์๋์ธ then, catch, finally ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
function promiseGet() {return new Promise({// ...});}// ํ๋ก๋ฏธ์ค ์ฒด์ด๋promiseGet().then(function(data) {// ...}).finally(function() {// ...}).catch(function(error) {// ...});// ES6 ํ์ดํ ํจ์ ๋ฌธ๋ฒpromiseGet().then(data => { /* ... */ }).finally(() => { /* ... */ }).catch(error => { /* ... */ });
Promise ์ ์ ๋ฉ์๋
Promise๋ ์ฃผ๋ก ์์ฑ์ ํจ์๋ก ์ฌ์ฉ๋์ง๋ง 5๊ฐ์ง ์ ์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. (ํจ์๋ ๊ฐ์ฒด์ด๋ฏ๋ก ๋ฉ์๋๋ฅผ ๊ฐ์ง ์ ์๋ค)
Promise์ 5๊ฐ์ง ์ ์ ๋ฉ์๋
- Promise.resolve()
- Promise.reject()
- Promise.all()
- Promise.race()
- Promise.allSettled()
Promise.resolve()
Promise.resolve()
๋ฉ์๋๋ ์ธ์๋ก ์ ๋ฌ๋ ๊ฐ์ resolveํ๋ ํ๋ก๋ฏธ์ค๋ฅผ ์์ฑํ๋ค.
(์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ์ ํ๋ก๋ฏธ์ค๋ก ๋ํํ๊ธฐ ์ํด ์ฌ์ฉ)
// ๋ฐฐ์ด์ resolveํ๋ Promise ๊ฐ์ฒด๋ฅผ ์์ฑconst resolvedPromise = Promise.resolve([1, 2, 3]);resolvedPromise.then(console.log); // [1, 2, 3]
์๋ ์ฝ๋๋ ์์ ๋์ผํ๋ค.
const resolvedPromise = new Promise(resolve => resolve([1, 2, 3]));resolvedPromise.then(console.log); // [1, 2, 3]
Promise.reject()
Promise.reject()
๋ฉ์๋๋ ์ธ์๋ก ์ ๋ฌ๋ ๊ฐ์ rejectํ๋ ํ๋ก๋ฏธ์ค๋ฅผ ์์ฑํ๋ค.
(์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ์ ํ๋ก๋ฏธ์ค๋ก ๋ํํ๊ธฐ ์ํด ์ฌ์ฉ)
// ์๋ฌ ๊ฐ์ฒด๋ฅผ rejectํ๋ Promise ๊ฐ์ฒด๋ฅผ ์์ฑconst rejectedPromise = Promise.reject(new Error('Error!'));rejectedPromise.catch(console.log); // Error: Error!
์๋ ์ฝ๋๋ ์์ ๋์ผํ๋ค.
const rejectedPromise = new Promise((resolve, reject) => reject(new Error('Error!')));rejectedPromise.catch(console.log); // Error: Error!
Promise.all()
Promise.all()
๋ฉ์๋๋ ํ๋ก๋ฏธ์ค๊ฐ ๋ด๊ฒจ ์๋ ๋ฐฐ์ด ๋ฑ์ ์ดํฐ๋ฌ๋ธ(์ํ/๋ฐ๋ณต ๊ฐ๋ฅํ)์ ์ธ์๋ก ์ ๋ฌ ๋ฐ๊ณ ํ๋ก๋ฏธ์ค๋ฅผ ๋ชจ๋ ์ฐ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ๊ทธ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ resolveํ๋ ์๋ก์ด ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค.
Promise.all([new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3]).then(console.log) // [ 1, 2, 3 ].catch(console.error);
Promise.all์ ๋ฐฐ์ด ๋ด ๋ชจ๋ ํ๋ก๋ฏธ์ค์ resolve ๋๋ ์ฒซ๋ฒ์งธ reject๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ , ๋ชจ๋ ํ๋ก๋ฏธ์ค์ ์ฒ๋ฆฌ๊ฐ ์ฑ๊ณตํ๋ฉด ๊ฐ๊ฐ์ ํ๋ก๋ฏธ์ค๊ฐ resolveํ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฐ์ด์ ๋ด์ resolveํ๋ ์๋ก์ด ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค.
์ฒ๋ฆฌ ์์ ๋ณด์ฅ
์ฒซ๋ฒ์งธ ํ๋ก๋ฏธ์ค๊ฐ ๊ฐ์ฅ ๋์ค์ ์ฒ๋ฆฌ๋์ด๋ Promise.all ๋ฉ์๋๊ฐ ๋ฐํํ๋ ํ๋ก๋ฏธ์ค๋ ์ฒซ๋ฒ์งธ ํ๋ก๋ฏธ์ค๊ฐ resolveํ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ถํฐ ์ฐจ๋ก๋๋ก ๋ฐฐ์ด์ ๋ด์ ๊ทธ ๋ฐฐ์ด์ resolveํ๋ ์๋ก์ด ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค.
ํ๋ก๋ฏธ์ค์ ์ฒ๋ฆฌ๊ฐ ์คํจํ๋ ๊ฒฝ์ฐ
์ฒ๋ฆฌ๊ฐ ํ๋๋ผ๋ ์คํจํ๋ฉด ๊ฐ์ฅ ๋จผ์ ์คํจํ ํ๋ก๋ฏธ์ค๊ฐ rejectํ ์๋ฌ๋ฅผ rejectํ๋ ์๋ก์ด ํ๋ก๋ฏธ์ค๋ฅผ ์ฆ์ ๋ฐํํ๋ค.
// ์ธ๋ฒ์งธ ํ๋ก๋ฏธ์ค๊ฐ ๊ฐ์ฅ ๋จผ์ ์คํจํ๋ฏ๋ก ์ธ๋ฒ์งธ ํ๋ก๋ฏธ์ค๊ฐ rejectํ ์๋ฌ๊ฐ catch ๋ฉ์๋๋ก ์ ๋ฌ๋๋ค.Promise.all([new Promise((resolve, reject) => setTimeout(() => reject(new Error('Error 1!')), 3000)),new Promise((resolve, reject) => setTimeout(() => reject(new Error('Error 2!')), 2000)),new Promise((resolve, reject) => setTimeout(() => reject(new Error('Error 3!')), 1000))]).then(console.log).catch(console.log); // Error: Error 3!
์ดํฐ๋ฌ๋ธ์ ์์๊ฐ ํ๋ก๋ฏธ์ค๊ฐ ์๋ ๊ฒฝ์ฐ
์ ๋ฌ ๋ฐ์ ์ดํฐ๋ฌ๋ธ์ ์์๊ฐ ํ๋ก๋ฏธ์ค๊ฐ ์๋ ๊ฒฝ์ฐ, Promise.resolve ๋ฉ์๋๋ฅผ ํตํด ํ๋ก๋ฏธ์ค๋ก ๋ํ๋๋ค.
Promise.all([1, // => Promise.resolve(1)2, // => Promise.resolve(2)3 // => Promise.resolve(3)]).then(console.log) // [1, 2, 3].catch(console.log);
Promise.race()
Promise.race()
๋ฉ์๋๋ ํ๋ก๋ฏธ์ค๊ฐ ๋ด๊ฒจ ์๋ ๋ฐฐ์ด ๋ฑ์ ์ดํฐ๋ฌ๋ธ์ ์ธ์๋ก ์ ๋ฌ ๋ฐ๊ณ ๊ฐ์ฅ ๋จผ์ ์ฒ๋ฆฌ๋ ํ๋ก๋ฏธ์ค๊ฐ resolveํ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ resolveํ๋ ์๋ก์ด ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค.
Promise.race([new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3]).then(console.log) // 3.catch(console.log);
ํ๋ก๋ฏธ์ค์ ์ฒ๋ฆฌ๊ฐ ์คํจํ๋ ๊ฒฝ์ฐ
์ฒ๋ฆฌ๊ฐ ํ๋๋ผ๋ ์คํจํ๋ฉด ๊ฐ์ฅ ๋จผ์ ์คํจํ ํ๋ก๋ฏธ์ค๊ฐ rejectํ ์๋ฌ๋ฅผ rejectํ๋ ์๋ก์ด ํ๋ก๋ฏธ์ค๋ฅผ ์ฆ์ ๋ฐํํ๋ค.
(Promise.all ๋ฉ์๋์ ๋์ผํ๊ฒ ์ฒ๋ฆฌ)
// ์ธ๋ฒ์งธ ํ๋ก๋ฏธ์ค๊ฐ ๊ฐ์ฅ ๋จผ์ ์คํจํ๋ฏ๋ก ์ธ๋ฒ์งธ ํ๋ก๋ฏธ์ค๊ฐ rejectํ ์๋ฌ๊ฐ catch ๋ฉ์๋๋ก ์ ๋ฌ๋๋ค.Promise.race([new Promise((resolve, reject) => setTimeout(() => reject(new Error('Error 1!')), 3000)),new Promise((resolve, reject) => setTimeout(() => reject(new Error('Error 2!')), 2000)),new Promise((resolve, reject) => setTimeout(() => reject(new Error('Error 3!')), 1000))]).then(console.log).catch(console.log); // Error: Error 3!
Promise.all vs Promise.race
Promise.all ๋ฉ์๋์ฒ๋ผ ๋ชจ๋ ํ๋ก๋ฏธ์ค๋ฅผ ์ฐ์์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์๋๋ผ ๊ฐ์ฅ ๋จผ์ ์ฒ๋ฆฌ๋ ํ๋ก๋ฏธ์ค๊ฐ resolveํ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ resolveํ๋ ์๋ก์ด ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค.
Promise.all()
: ๋ชจ๋ ํ๋ก๋ฏธ์ค๋ฅผ ์ฐ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์์๋๋ก ๋ชจ๋ ๋ฐํPromise.race()
: ๊ฐ์ฅ ๋จผ์ ์ฒ๋ฆฌ๋ ํ๋ก๋ฏธ์ค๋ง ๋ฐํ
Promise.allSettled()
Promise.allSettled()
๋ ํ๋ก๋ฏธ์ค๊ฐ ๋ด๊ฒจ ์๋ ๋ฐฐ์ด ๋ฑ์ ์ดํฐ๋ฌ๋ธ์ ์ธ์๋ก ์ ๋ฌ ๋ฐ๊ณ ๋ชจ๋ ํ๋ก๋ฏธ์ค๋ฅผ ๋ชจ๋ ์ฐ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ํ์ ๊ด๊ณ์์ด ๊ทธ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฐ์ด๋ก ๋ฐํํ๋ค.
Promise.allSettled([new Promise(resolve => setTimeout(() => resolve(1), 2000)),new Promise((resolve, reject) => setTimeout(() => reject(new Error('Error!')), 1000))]).then(console.log);
ํ๋ก๋ฏธ์ค์ ์ฒ๋ฆฌ ์ํ์ ์๊ด์์ด ๋ชจ๋ ๋ฐํ
fulfilled ๋๋ rejected ์๊ด์์ด ์ธ์๋ก ์ ๋ฌ๋ฐ์ ๋ชจ๋ ํ๋ก๋ฏธ์ค๋ค์ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ ๊ฐ์ฒด๋ฅผ ๋ด์ ๋ฐฐ์ด์ ๋ฐํํ๋ค.
// Promise.allSettled ๋ฉ์๋๊ฐ ๋ฐํํ๋ ๋ฐฐ์ด[// ํ๋ก๋ฏธ์ค๊ฐ fulfilled ์ํ์ธ ๊ฒฝ์ฐ, ํ๋ก๋ฏธ์ค์ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ{status: "fulfilled", value: 1},// ํ๋ก๋ฏธ์ค๊ฐ rejected ์ํ์ธ ๊ฒฝ์ฐ, ํ๋ก๋ฏธ์ค์ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ{status: "rejected", reason: Error: Error! at ...}]
- fulfilled ์ํ์ธ ๊ฒฝ์ฐ: ์ฒ๋ฆฌ ์ํ๋ฅผ ๋ํ๋ด๋ status ํ๋กํผํฐ, ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ value ํ๋กํผํฐ
{status: "fulfilled", value: /* ๊ฒฐ๊ณผ ๊ฐ */ }
- rejected ์ํ์ธ ๊ฒฝ์ฐ: ์ฒ๋ฆฌ ์ํ๋ฅผ ๋ํ๋ด๋ status ํ๋กํผํฐ, ์๋ฌ๋ฅผ ๋ํ๋ด๋ reason ํ๋กํผํฐ
{status: "rejected", reason: /* ์๋ฌ ๋ฉ์์ง */ }
TC39 Stage 4 Draft
Expected Publication Year: 2020Promise.allSettled() - JavaScript | MDN / tc39/proposal-promise-allSettled
Callback ํจํด๊ณผ Promise
๋น๋๊ธฐ ์ฒ๋ฆฌ ์์๋ฅผ ๋ณด์ฅํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ์ ํต์ ์ธ ์ฝ๋ฐฑ ํจํด์ ๊ฐ๋ ์ฑ์ด ๋์๊ณ ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ค ๋ฐ์ํ ์๋ฌ์ ์์ธ ์ฒ๋ฆฌ๊ฐ ๊ณค๋ ํ๋ฉฐ ์ฌ๋ฌ ๊ฐ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ๋ฒ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ํ๊ณ๊ฐ ์๋ค.
Callback Hell
๋น๋๊ธฐ ํจ์์ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ง๊ณ ๋ค๋ฅธ ๋น๋๊ธฐ ํจ์๋ฅผ ํธ์ถํด์ผ ํ๋ ๊ฒฝ์ฐ, ํจ์์ ํธ์ถ์ด ์ค์ฒฉ(nesting)์ด ๋์ด ๋ณต์ก๋๊ฐ ๋์์ง๋ ํ์์ด ๋ฐ์ํ๋๋ฐ ์ด๋ฅผ ์ฝ๋ฐฑ ํฌ(Callback Hell)์ด๋ผ ํ๋ค.
step1(function(value1) {step2(value1, function(value2) {step3(value2, function(value3) {step4(value3, function(value4) {step5(value4, function(value5) {// value5๋ฅผ ์ฌ์ฉํ๋ ์ฒ๋ฆฌ});});});});});
get('/step1', a => {get(`/step2/${a}`, b => {get(`/step3/${b}`, c => {get(`/step4/${c}`, d => {console.log(d);});});});});
Callback ํจํด๊ณผ Promise ๋น๊ต
callback ํจํด์ผ๋ก ๊ตฌํํ ์ฝ๋
// GET ์์ฒญ์ ์ํ ๋น๋๊ธฐ ํจ์const get = (url, callback) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.send();xhr.onload = () => {if (xhr.status === 200) {// ์๋ฒ๋ก๋ถํฐ์ ์๋ต์ ์ฝ๋ฐฑ ํจ์์ ์ ๋ฌํ๋ฉด์ ํธ์ถํ์ฌ ์๋ต์ ๋ํ ํ์ ์ฒ๋ฆฌ๋ฅผ ํ๋ค.callback(JSON.parse(xhr.response));} else {console.error(`${xhr.status} ${xhr.statusText}`);}};};// id๊ฐ 1์ธ post์ userId๋ฅผ ์ทจ๋get('https://jsonplaceholder.typicode.com/posts/1', ({ userId }) => {// ์ทจ๋ํ post์ userId๋ก user ์ ๋ณด๋ฅผ ์ทจ๋get(`https://jsonplaceholder.typicode.com/users/${userId}`, userInfo => {console.log(userInfo);});});
์์ ์์ ๋ฅผ promise๋ก ๊ตฌํํ ์ฝ๋
// GET ์์ฒญ์ ์ํ ๋น๋๊ธฐ ํจ์const promiseGet = url => {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.send();xhr.onload = () => {if (xhr.status === 200) {// ์ฑ๊ณต์ ์ผ๋ก ์๋ต์ ์ ๋ฌ๋ฐ์ผ๋ฉด resolve ํจ์๋ฅผ ํธ์ถํ๋ค.resolve(JSON.parse(xhr.response));} else {// ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ํด reject ํจ์๋ฅผ ํธ์ถํ๋ค.reject(new Error(xhr.status));}};});};// id๊ฐ 1์ธ post์ userId๋ฅผ ์ทจ๋promiseGet('https://jsonplaceholder.typicode.com/posts/1')// ์ทจ๋ํ post์ userId๋ก user ์ ๋ณด๋ฅผ ์ทจ๋.then(({ userId }) => promiseGet(`https://jsonplaceholder.typicode.com/users/${userId}`)).then(userInfo => console.log(userInfo)).catch(err => console.error(err));
fetch
fetch()
ํจ์๋ XMLHttpRequest ๊ฐ์ฒด์ ๋ง์ฐฌ๊ฐ์ง๋ก HTTP ์์ฒญ ์ ์ก ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ Web API, HTTP ์๋ต์ ๋ํ๋ด๋ Response ๊ฐ์ฒด๋ฅผ ๋ํํ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ฏ๋ก ํ์ ์ฒ๋ฆฌ ๋ฉ์๋ then์ ํตํด ํ๋ก๋ฏธ์ค๊ฐ resolveํ Response ๊ฐ์ฒด๋ฅผ ์ ๋ฌ๋ฐ์ ์ ์๋ค.
fetch()
์ HTTP ์์ฒญ์ ์ ์กํ URL๊ณผ HTTP ์์ฒญ ๋ฉ์๋, HTTP ์์ฒญ ํค๋, ํ์ด๋ก๋ ๋ฑ์ ์ค์ ํ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ค.
const promise = fetch(url [, options]);
fetch ํจ์๋ XMLHttpRequest ๊ฐ์ฒด๋ณด๋ค ์ฌ์ฉ๋ฒ์ด ๊ฐ๋จํ๊ณ ํ๋ก๋ฏธ์ค๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฝ๋ฐฑ ํจํด์ ๋จ์ ์์ ์์ ๋กญ๋ค.
Response ๊ฐ์ฒด
HTTP ์๋ต์ ๋ํ๋ด๋ Response ๊ฐ์ฒด๋ฅผ ๋ํํ Promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ณ ๋ค์ํ ํ๋กํผํฐ๋ฅผ ์ ๊ณตํ๋ค.
Response ๊ฐ์ฒด๊ฐ ๊ฐ์ง๋ ๊ธฐ๋ณธ์ ์ธ ํ๋กํผํฐ
Response.status
: HTTP Status ์ฝ๋, HTTP ์ํ/์๋ต ์ฝ๋ (๊ธฐ๋ณธ๊ฐ 200)Response.statusText
: HTTP Status ์ฝ๋์ ๋ฉ์๋์ ์ผ์นํ๋ ๋ฌธ์์ด (๊ธฐ๋ณธ๊ฐ "OK")Response.ok
: HTTP Status ์ฝ๋๊ฐ 200์์ 299์ค ํ๋์์ ์ฒดํฌํ๋ ๊ฐ (Boolean ๋ฐํ)
Response.prototype์๋ Response ๊ฐ์ฒด์ ํฌํจ๋์ด ์๋ HTTP ์๋ต ๋ชธ์ฒด(body)๋ฅผ ์ํ ๋ค์ํ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
์๋ฅผ ๋ค์ด, fetch ํจ์๊ฐ ๋ฐํํ ํ๋ก๋ฏธ์ค๊ฐ ๋ํํ๊ณ ์๋ HTTP ์๋ต ๋ชธ์ฒด๋ฅผ ์ทจ๋ํ๋ ค๋ฉด
Response.prototype.json
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.Response.prototype.json ๋ฉ์๋
Response ๊ฐ์ฒด์์ HTTP ์๋ต ๋ชธ์ฒด(response.body)๋ฅผ ์ทจ๋ํ์ฌ ์ญ์ง๋ ฌํ ํ๋ค.
Request Option ์ ์ฉ
fetch ํจ์์ ์์ฒญ ์ต์ ์ ์ค์ ํด ๋ค์ํ ์์ฒญ์ ์ ์กํ ์ ์๋ค.
- ์ฒซ ๋ฒ์งธ ์ธ์: HTTP ์์ฒญ์ ์ ์กํ URL
- ๋ ๋ฒ์งธ ์ธ์: HTTP ์์ฒญ ๋ฉ์๋, HTTP ์์ฒญ ํค๋, ํ์ด๋ก๋ ๋ฑ์ ์ค์ ํ ๊ฐ์ฒด
const request = fetch('https:// ...', // HTTP ์์ฒญ์ ์ ์กํ URL{method: 'POST', // HTTP ์์ฒญ ๋ฉ์๋headers: { 'content-Type': 'application/json' }, // HTTP ์์ฒญ ํค๋body: JSON.stringify(payload) // ํ์ด๋ก๋});
// ์์ฒญ ์ค์ ๊ฐ์ฒด, *ํ์๋ ๊ธฐ๋ณธ ์ต์ {method: 'POST', // *GET, POST, PUT, DELETE, etc.mode: 'cors', // no-cors, cors, *same-origincache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cachedcredentials: 'same-origin', // include, *same-origin, omitheaders: {'Content-Type': 'application/json',// 'Content-Type': 'application/x-www-form-urlencoded',},redirect: 'follow', // manual, *follow, errorreferrer: 'no-referrer', // no-referrer, *clientbody: JSON.stringify(data), // body data type must match "Content-Type" header}
fetch ์ฌ์ฉ ์์
const request = {get(url) {return fetch(url);},post(url, payload) {return fetch(url, {method: 'POST',headers: { 'content-Type': 'application/json' },body: JSON.stringify(payload)});},patch(url, payload) {return fetch(url, {method: 'PATCH',headers: { 'content-Type': 'application/json' },body: JSON.stringify(payload)});},delete(url) {return fetch(url, { method: 'DELETE' });}};request.get('https://jsonplaceholder.typicode.com/todos/1').then(response => response.json()).then(todos => console.log(todos)).catch(err => console.error(err));// {userId: 1, id: 1, title: "delectus aut autem", completed: false}request.post('https://jsonplaceholder.typicode.com/todos', {userId: 1,title: 'JavaScript',completed: false}).then(response => response.json()).then(todos => console.log(todos)).catch(err => console.error(err));// {userId: 1, title: "JavaScript", completed: false, id: 201}request.patch('https://jsonplaceholder.typicode.com/todos/1', {completed: true}).then(response => response.json()).then(todos => console.log(todos)).catch(err => console.error(err));// {userId: 1, id: 1, title: "delectus aut autem", completed: true}request.delete('https://jsonplaceholder.typicode.com/todos/1').then(response => response.json()).then(todos => console.log(todos)).catch(err => console.error(err));// {}
fetch ํจ์๋ ๋น๊ต์ ์ต๊ทผ์ ์ถ๊ฐ๋ Web API๋ก์ ์ธํฐ๋ท ์ต์คํ๋ก์ด๋ฅผ ์ ์ธํ ๋๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ์์ ์ ๊ณตํ๊ณ ์๋ค.
reference
์๋ฐ์คํฌ๋ฆฝํธ Promise ์ฝ๊ฒ ์ดํดํ๊ธฐ โข Captain Pangyo