JavaScript 프로미스
1. Promise
비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트이다.
-
프로미스는 자바스크립트 안에 내장되어 있는 오브젝트이며, 비동기적인 것을 수행할 때 콜백함수 대신 유용하게 사용할 수 있는 오브젝트이다.
-
정해진 장시간의 기능을 수행한 뒤, 정상적으로 기능이 수행되었다면 성공메세지와 함께 처리된 결과값을 전달하고, 기능을 수행하다가 문제가 발생한다면 에러를 전달해준다.
-
네트워크통신이나 파일을 읽어 오는 것 등 어떤 무거운 일을 처리할 때 사용 될 수 있다.
→ 네트워크에서 데이터를 받아오거나 파일에서 큰 데이터를 읽어오는 과정은 다소 시간이 소요된다. 이를 만약 동기적으로 수행한다면 다음 라인의 코드가 실행되지 않기 때문에, 프로미스를 통해 비동기적으로 처리하는 것이 좋다.
📌 Promise 포인트 2가지
(1) state(상태)
프로세스가 무거운 작업을 수행하고 있는 중인지, 혹은 기능 수행을 완료(성공or실패) 했는지에 대한 상태(state)를 이해한다.
- promise는 3가지 상태를 가진다.
→pending
: 대기 상태로서 아직 resolve 할지 reject 할 지 결정되지 않은 초기의 상태이다.
→fulfilled
: 이행 상태로써 연산이 성공적으로 완료된 상태이다.
→rejected state
: 거부 상태로써 (파일을 찾을 수 없거나 네트워크에 문제가 생긴 이유 등) 실패된 상태이다.
✍️ pending 상태 예시 ▼
function fetchUser() {
return new Promise((resolve, reject) => {
return ('seul');
})
}
const user = fetchUser();
console.log(user);
//resolve나 reject를 호출하지 않았기 때문
✍️ fulfilled 상태 예시 ▼
function fetchUser() {
return new Promise((resolve, reject) => {
resolve('seul');
})
}
const user = fetchUser();
console.log(user);
(2) producer & consumer
producer와 consumer의 차이에 대해 이해한다.
-
producer
: 원하는 데이터를 제공한다. -
consumer
: 제공된 데이터를 필요로 한다. 즉 데이터를 소비하는 측이다.
2. then & catch & finally
then()
- then() 는 Promise를 반환하고 두 개의 콜백 함수를 인수로 받는다. (이행했을 때와 거부했을 때를 위한 콜백 함수)
- promise 가 종료가 되면 resolve 에 들어간 값을 받을 수 있다.
- reject 된 경우에 then으로 받으면 에러가 발생한다.
// ex.1) resolve와 then
const promise = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve("resolve");
}, 3000);
});
promise.then(value => console.log(value));
// 3초 후에 결과가 출력
// resolve
// ex.2) reject와 then
const promise = new Promise((resolve, reject)=>{
setTimeout(()=>{
reject("reject");
}, 3000);
});
promise.then(value => console.log(value));
// UnhandledPromiseRejectionWarning: reject
catch()
- catch() 는 Promise를 반환하고 거부(실패)된 경우만 처리된다.
- 위의
ex.2)
의 경우, catch를 사용해서 에러를 잡아줄 수 있다. ex.3)
catch가 then 뒤에 연결되어 있다고 해서 then이 실행된 후 catch가 실행되는 것은 아니다.
→ 둘은 같이 실행되지 않으며 resolve 시에는 then이, reject 시에는 catch가 실행되는 것이다.
// ex.3) reject와 catch
const promise = new Promise((resolve, reject)=>{
setTimeout(()=>{
reject("reject");
}, 3000);
});
promise.then(result => console.log(result)).catch(error => console.log(error));
// 3초 후에 결과가 출력
// reject
then() & catch()
then
과catch
메서드는 프로미스를 반환하기 때문에, 체이닝이 가능하다.
finally()
- finally() 는 Promise를 반환하고 성공 실패와 상관없이 마지막에 무조건 호출된다.
3. Promise 생성 & 활용
PromiseConstructor
- 프로미스는 클래스이기때문에
new
라는 키워드를 이용해서 오브젝트를 생성할 수 있다. - Promise는 매개변수로 executor를 받고, executor는 resolve와 reject 인수를 전달할 실행함수이다.
→ 즉, 프로미스의 생성자를 보면executor
라는 콜백함수를 전달해 주어야 한다. 이 콜백함수에는 또 다른 두가지 콜백함수를 받는다. ▼
(1) resolve : 기능을 정상적으로 수행해서 마지막에 최종 데이터를 전달한다.
(2) reject : 기능을 수행하다가 중간에 문제가 생기면 호출한다.
3-1. Promise 만들기
✍️ 예제를 작성해보자.
const promise = new Promise((resolve, reject) => {
console.log('doing something...'); // 즉시 실행된다.
});
// result ▼
// doing something
▲ ❗️ new Promise
가 만들어 질 때는, 전달한 executor
라는 콜백 함수가 즉시 실행된다.
- 위의
console.log
가 즉시 실행된다는 것은 프로미스를 만드는 순간 전달한 executor라는 콜백함수가 바로 실행된다는 의미이다. - 즉, 만약 사용자가 버튼을 눌렀을 때 비로소 네트워크 요청 등을 필요로 한다면, 불필요한 네트워크 통신이 이루어질 수도 있다는 점을 주의해야 한다.
(1) resolve & reject
// Producer
const promise = new Promise((resolve, reject) => {
console.log('doing something...');
setTimeout(() => {
resolve('seul');
}, 2000)
});
// Consumers
promise.then((value) => {
console.log(value);
});
// result ▼
// 'doing something...'
// (2초뒤) seul
▲ then
은 프로미스가 정상적으로 수행되어서 resolve
라는 콜백함수를 통해서 전달한 값이 value
의 파라미터로 전달되어 들어온 것이다.
(2) resolve & reject
// Producer
const promise = new Promise((resolve, reject) => {
console.log('doing something...');
setTimeout(() => {
// resolve('seul');
reject(new Error('이유를 잘 명시한다. ex) no network'))
}, 2000)
});
// Consumers
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
})
.finally(()=> {console.log('finally')})
;
// result ▼
// 'doing something...'
// (2초뒤) Error: 이유를 잘 명시한다. ex) no network
// finally
▲ 보통 에러라는 오브젝트를 통해서 값을 전달한다. Error
라는 클래스는 자바스크립트에서 제공하는 오브젝트중 하나이다. MDN-Error생성자
then
,catch
,finally
를 이용해서 값을 받아온다.value
파라미터는 프로미스가 잘 수행 되어서 마지막으로 전달된 값이 들어온다.- Producer ) 성공적으로 수행되었다면 resolve를 호출, 실패했다면 reject를 전달한다.
- Consumers ) promise를 이용해서 (then, catch, finally) 성공한 값 혹은 실패한 에러를 가져와서 원하는 방식으로 처리할 수 있다.
3-2. 프로미스 체이닝(chaining)
// 서버에서 숫자를 받아오는 프로미스
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(()=> resolve(1), 1000); // 1초후에 숫자 1 을전달
});
fetchNumber
.then(num => num*2)
.then(num => num*3)
.then(num => {
return new Promise((resolve, reject) => {
setTimeout(()=> resolve(num-1), 1000);
})
})
.then(num => console.log(num));
// result ▼
// (2초뒤) 5
then
은 값을 바로 전달할 수도 있고,Promise
를 전달해도 된다. 이처럼then
을 여러번 묶어서 처리할 수도 있다.- 프로미스 체이닝이 가능한 이유는
promise.then
을 호출하면 프로미스가 반환되기 때문이며, 반환된 프로미스엔 당연히.then
을 호출할 수 있다.
3-3. 프로미스 체이닝 + 에러 핸들링(Error Handling)
(1) 프로미스 체이닝
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));
// result ▼
// 🐓 => 🥚 => 🍳
- 콜백함수를 전달할 때, 받아온 value를 다른 함수로 바로 하나로 호출할 경우에는 아래처럼 생략 가능하다.
→getHen().then(getEgg).then(cook).then(console.log);
(2) 에러 핸들링
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = hen =>
new Promise((resolve, reject) =>{
setTimeout(() => reject(new Error(`${hen} => 🥚`)), 1000);
});
const cook = egg =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});
getHen()
.then(getEgg)
// 이부분에서 발생하는 에러를 처리하고 싶다면
// 바로 뒤에 catch를 작성해서 바로바로 문제를 해결할 수 있다.
.catch(error => {
return '🧀';
})
.then(cook)
.then(console.log)
.catch(console.log);
// result ▼
// 🧀 => 🍳
- 일부분에 문제가 발생하더라도 전체적인 처리에 문제가 발생하지 않도록 컨트롤 해줄 수 있다.
- 마지막 행의
catch
는 도중에 에러가 발생했고, 에러처리가 안된 상황이라면, 해당 에러가 전달되어서 마지막에 호출된다.
(위의 예제에서는 에러발생 지점 바로 뒤에catch
를 작성해서 문제해결을 바로 한 것이다.🙂)
✍️ 궁금했던 것
❓then
은 성공했을 경우에만 실행되는 것 같은데, 그럼 왜 인수를 두 가지로 받는다는 설명이 있을까?🤔
then()
메소드는 두개의 콜백 함수의 인수를 받고 이행 및 거부 여부에 따라서 다르게 실행된다.then()
문법 ▼
promise.then(
function(result) { /* 결과(result)를 다룬다 */ },
function(error) { /* 에러(error)를 다룬다 */ }
);
- 성공적으로 이행된 프로미스 ▼
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000);
});
// resolve 함수는 .then의 첫 번째 함수(인수)를 실행한다.
promise.then(
result => alert(result), // 1초 후 "done!"을 출력한다.
error => alert(error) // 실행되지 않는다.
);
- 프로미스가 거부된 경우 ▼
let promise = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// reject 함수는 .then의 두 번째 함수를 실행한다.
promise.then(
result => alert(result), // 실행되지 않는다.
error => alert(error) // 1초 후 "Error: 에러 발생!"를 출력한다.
);
❓ 그러면 catch
와는 어떻게 다를까?
catch(errorHandlingFunction)
를 써도 된다.catch
는then
에null
을 전달하는 것과 동일하게 작동한다.- 즉,
then
으로 에러가 발생한 경우만 다루고 싶다면then(null, errorHandlingFunction)
같이null
을 첫 번째 인수로 전달하면 된다.
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// .catch(f)는 promise.then(null, f)과 동일하게 작동한다.
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!"을 출력한다.
✍️ 작업이 성공적으로 처리된 경우만 다루고 싶다면 then
에 인수를 하나만 전달하면 된다. 🙂
✍️ .catch(f)
는 문법이 간결하다는 점만 빼고 .then(null,f)
과 같다.
reference
MDN-Error생성자
MDN-then
MDN-catch
MDN-promise
dream coding
promise
js-promise
Author And Source
이 문제에 관하여(JavaScript 프로미스), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@seul06/JavaScript-프로미스저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)