JavaScript(10) - promise
JavaScript 관련 포스팅
- JavaScript(1) - async vs. defer
- JavaScript(2) - variable
- JavaScript(3) - operator
- JavaScript(4) - function
- JavaScript(5) - class vs. object
- JavaScript(6) - object
- JavaScript(7) - array
- JavaScript(8) - JSON
- JavaScript(9) - callback
- JavaScript(10) - promise
- JavaScript(11) - Regular expressions
- JavaScript(12) - Prototype과 Class 피하기
- JavaScript(13) - localStorage
promise
- 비동기(asynchronous)를 callback fn 대신 간편하게 처리하도록 도와주는 내장 object
- 정해진 장시간의 기능을 수행 후
- 정상적으로 기능이 수행이 되었다면, 성공 메시지와 처리된 결괏값을 전달해줌
- 만약 기능 수행 중 예상치 못한 문제가 발생하였다면, 에러를 전달해줌
포인트
-
State [상태] 이해
프로세스가 heavy한 명령을 수행하고 있는지, 기능 수행 완료 후 성공했는지, 실패했는지 -
Producer vs. Consumer
Producer: 우리가 원하는 데이터를 제공하는 사람
Consumer: 제공된 데이터를 쓰는/필요로 하는 사람
State [promise의 상태]
promise가 만들어져서,
- 지정한 operation을 수행하고 있는 중이라면 pending상태
- operation을 성공적으로 끝내게 되면 fulfilled상태
- file을 찾을 수 없거나 네트워크에 문제가 생기면 rejected상태
Producer vs. Consumer
- Producer
원하는 기능을 수행해 해당하는 데이터를 만들어내는 promise object - Consumer
원하는 데이터를 소비
1. Producer
- Promise 만들기
- class라서
new
키워드를 이용해 object를 생성할 수 있음 - promise 생성자를 보면
executor
라는 callback fn을 전달해주어야 함executor
는resolve
(기능이 정상 수행되어 결괏값을 전달함)와
reject
(기능 수행 중 문제가 생기면 호출함) 두 개의 callback fn으로 구성됨
var Promise: PromiseConstructor new <any>(executor: (resolve: (value: any) => void, reject: (reason?: any) => void) => void) => Promise<any>
const promise = new Promise((resolve, reject) => {
// doing some heavy work(network, read files)
console.log('doing something...');
setTimeout(() => {
resolve('success!');
}, 2000);
});
→ 어떤 일을 2000ms(=2sec)정도 하다가 잘 마무리 해서 resolve
(callback fn)를 호출하여 ‘success!’라는 값을 전달하는 Promise
- promise안에서는 다소 heavy한 일을 함
네트워크에서 데이터를 받아오거나 파일에서 큰 데이터를 읽어오는 과정은 시간이 꽤 걸림
시간이 많이 소요되는 과정들을 과정들을 동기적(synchronous)으로 처리하는 동안에 다음 라인의 코드는 실행되지 않게 됨
그래서 시간이 좀 걸리는 일들은 Promise를 만들어 비동기적으로 처리하는 것이 좋음
※주의※
Promise를 만드는 순간, 우리가 만든 executor(callback fn)가 바로! 자동 실행됨
2. Consumer
- producer를 사용하는 주체
-
then
,catch
,finally
로 값을 받아옴promise.then((value) => { console.log(value); })
⇒ 위에서 만든
promise
가 정상적으로 수행이 된다면(then
),
value
라는 parameter를 받아오는 callback fn을 수행하게 됨※ 여기서 value는
resolve
(callback fn)으로 전달된 ‘success!’라는 값
Console
doing something…
success!
resolve
가 아닌 reject
를 사용한다면?
- reject는 주로 Error object를 통해 값을 전달함
-
Error object에는 어떤 error가 발생했는지 이유를 잘 명시해야함
const promise = new Promise((resolve, reject) => { // doing some heavy work(network, read files) console.log('doing something...'); setTimeout(() => { // resolve('success!'); reject(new Error('no network')); }, 2000); });
Console
doing something…
▶ Uncaught (in promise) Error: no network at promise.js:16:16
- console장에
Uncaught
(잡히지 않는)라고 뜨는 이유는then
으로 성공적인 케이스만 다뤘기 때문임
→ catch
를 이용해 error가 발생했을 때 어떻게 처리할 지 callback fn을 등록해주면 됨
promise
.then((value) => {
console.log(value);
})
.catch(error => {
console.log(error);
});
Console
doing something…
Error: no network at promise.js:15:16
⇒ catch
의 등록으로 더는 error를 생성하지 않게 됨
then과 catch를 chaining
- promise의
then
을 호출하면, 똑같은 Promise를 리턴함
리턴된 Promise에catch
를 호출할 수 있게 됨
(array에서 map, sort, join함수를 중첩 사용하는 것도 chaining)
(method) Promise<any>.then<void, never>(onfulfilled?: (value: any) => void | PromiseLike<void>, onrejected?: (reason: any) => PromiseLike<never>): Promise<void>
finally는 성패와 상관 없이 무조건 마지막에 호출됨
const promise = new Promise((resolve, reject) => {
console.log('doing something...');
setTimeout(() => {
resolve('success!');
// reject(new Error('no network'));
}, 2000);
});
promise
.then((value) => {
console.log(value);
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log('finally!');
});
Console
doing something…
success!
finally!
const promise = new Promise((resolve, reject) => {
console.log('doing something...');
setTimeout(() => {
// resolve('success!');
reject(new Error('no network'));
}, 2000);
});
promise
.then((value) => {
console.log(value);
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log('finally!');
});
Console
doing something…
Error: no network at promise.js:15:16
finally!
3. Promise chaining
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000); // 1 전달
});
fetchNumber
.then(num => num * 2) // 1 * 2 = 2
.then(num => num * 3) // 2 * 3 = 6
.then(num => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(num -1), 1000);
}); // 6 - 1 = 5
})
.then(num => console.log(num)); // 5
Console
5
4. Error handling
Original code
const getHen = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = hen => new Promise((resolve, reject) => {
setTimeout(() => resolve(`${hen} => 🥚`), 1000);
});
const cook = egg => new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen()
.then(hen => getEgg(hen))
.then(egg => cook(egg))
.then(meal => console.log(meal));
Console
🐓 => 🥚 => 🍳
tip
.then
에서 받아오는 value를 바로 callback fn의 parameter로 전달할 때, 중복되는 value는 생략이 가능함
getHen()
.then(getEgg)
.then(cook)
.then(console.log)
Error 발생
const getHen = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = hen => new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000);
});
const cook = egg => new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
Console
▶ Uncaught ReferenceError: Cannot access ‘getHen’ before initialization at promise.js:62:1
Error를 .catch
로 잡아주면,
getHen()
.then(getEgg)
.then(cook)
.then(console.log)
.catch(console.log)
Console
Error: error! 🐓 => 🥚 at promise.js:71:29
.then
뒤에 .catch
로 바로 error를 처리해주면 됨
getHen()
.then(getEgg)
.catch(error => {
return '🥖';
})
.then(cook)
.then(console.log)
.catch(console.log)
Console
🥖 => 🍳
< 출처 >
“자바스크립트 12. 프로미스 개념부터 활용까지 JavaScript Promise | 프론트엔드 개발자 입문편 (JavaScript ES6),” 유튜브 비디오, 28:09, 게시자 “드림코딩 by 엘리,” 2020년 6월 18일, https://youtu.be/tJieVCgGzhs