Promise
- Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과값을 나타낸다.
- ES6부터 Javascript의 표준 내장 객체로 추가되었다.
- ES6를 지원하는 브라우저나 Node.js에서 전역에 있는 Promise를 확인할 수 있다.
생성자로 프로미스 객체 생성
생성자의 인자로 executor
라는 함수를 이용한다.
executor
함수는 resolve
와 reject
를 인자로 가진다.
new Promise(/*executor*/ (resolve, reject) => {});
1. Pending(대기) 상태
생성자를 통해서 프로미스 객체를 만드는 순간 pending (대기) 상태라고 한다.
new Promise((resolve, reject) => {}); // pending
2. Fulfilled(이행) 상태
executor 함수 인자 중 하나인 resolve 함수를 실행하면, fulfilled (이행) 상태가 된다.
new Promise((resolve, reject) => {
//
// ... 비동기적 상황 처리
resolve(); // fulfilled
});
3. Rejected(거부) 상태
executor 함수 인자 중 하나인 reject 함수를 실행하면, rejected (거부) 상태가 된다.
new Promise((resolve, reject) => {
reject(); // rejected
});
new Promise를 통해서 executor 함수를 인자로 받아 객체를 생성했을 때 pending 상태로 들어간다.
비동기적 상황처리가 이뤄지는 동안에는 계속 pending을 유지한다.
비동기적 처리가 끝나고 임무를 완수한 뒤 fulfilled (이행 상태)로 넘어가게 하기 위해 resolve( )
함수를 호출한다.
제대로 이행되지 않고 문제가 생겨서 error 상황이 발생했을 때는 executor의 두번째 인자로 들어온 reject( )
함수를 호출해서 rejected (거부 상태)로 만들어줄 수 있다.
예) p라는 프로미스 객체는 1,000ms 후에 fulfilled 된다.
setTimeout( )
함수를 통해서 ms 이후에 실행되는 함수를 설정해줄 수 있다.
new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
resolve(); /* fulfilled */
}, 1000);
});
const p = new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
resolve(); /* fulfilled */
}, 1000);
});
p.then(() => {
console.log('1000ms 후에 fulfilled 된다.');
}); // 1000ms 후에 fulfilled 된다.
실무에서 사용할 때는 객체를 바로 만들어서 사용하기 보다는 실제로 사용하는 곳에서 객체를 생성해서 then과 연결해주는 방식으로 작업한다.
then을 설정하는 시점을 정확하고, '함수의 실행과 동시에 프로미스 객체를 만들면서 pending이 시작하도록 하기 위해 프로미스 객체를 생성하면서 리턴하는 함수(p)를 만들어, 함수(p) 실행과 동시에 then을 설정한다.
function p() {
return new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
resolve(); /* fulfilled */
}, 1000);
});
}
p().then(() => {
console.log('1000ms 후에 fulfilled 된다.');
});
rejected와 catch(callback)
마찬가지로 프로미스 객체와 rejected 되는 시점에 p.catch
안에 설정한 callback 함수가 실행된다.
then으로 가는 것이 아닌, rejected가 불리면 연결될 함수(catch)를 설정 - 체인이 가능한 형태 catch(/*callback*/)
function p() {
return new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
reject(); /* rejected */
}, 1000);
});
}
p().then(() => {
console.log('1000ms 후에 fulfilled 된다.');
}).catch(() => {
console.log('1000ms 후에 rejected 된다.');
});
resolve 함수 인자를 then으로 데이터 전달
executor의 resolve 함수를 실행할 때 인자를 넣어 실행하면, then의 callback 함수의 인자로 받을 수 있다.
resolve('hello');
then((message) => {...})
function p() {
return new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
resolve('hello');
}, 1000);
});
}
p()
.then((message) => {
console.log('1000ms 후에 fulfilled 된다.', message);
})
.catch(() => {
console.log('1000ms 후에 rejected 된다.');
});
reject 함수 인자를 catch로 데이터 전달
마찬가지로 executor의 reject 함수를 실행할 때 인자를 넣어 실행하면, catch의 callback 함수의 인자로 받을 수 있다.
reject('error');
then((reason) => {...})
error가 reason으로 넘어간 것을 확인할 수 있다.
function p() {
return new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
reject('error');
}, 1000);
});
}
p()
.then((message) => {
console.log('1000ms 후에 fulfilled 된다.', message);
})
.catch((reason) => {
console.log('1000ms 후에 rejected 된다.', reason);
});
// 1000ms 후에 rejected 된다. error
reject와 Error 객체 생성
보통 reject 함수를 실행하며 rejected 되는 이유를 넘기는데, 표준 내장 객체인 Error의 생성자를 이용하여 Error 객체를 만들 수 있다.
실행하면 reject 되면서 생성된 error 객체가 catch callback 함수의 인자로 넘어간다.
Error 객체의 상태나 코드를 적어서 넘겨서 처리한다.
function p() {
return new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
reject(new Error('bad'));
}, 1000);
});
}
p()
.then((message) => {
console.log('1000ms 후에 fulfilled 된다.', message);
})
.catch((error) => {
console.log('1000ms 후에 rejected 된다.', error);
});
reject 후 finally 실행
fulfilled 되거나 rejected 된 후에 최종적으로 실행할 것이 있다면, .finally( ) 를 설정하고, 함수를 인자로 넣는다.
function p() {
return new Promise((resolve, reject) => {
/* pending */
setTimeout(() => {
reject(new Error('bad'));
}, 1000);
});
}
p()
.then((message) => {
console.log('1000ms 후에 fulfilled 된다.', message);
})
.catch((error) => {
console.log('1000ms 후에 rejected 된다.', error);
})
.finally(() => {
console.log('end');
}); // (rejected 된 error 객체 로그 출력) end
비동기 작업을 callback 으로 할 때
보통 비동기 작업을 할 때, callback 함수를 인자로 넣어 로직이 끝나면 callback 함수를 호출한다.
이런 경우 함수가 아래로 진행되지 않고, callback 함수 안으로 진행된다.
function c(callback) {
setTimeout(() => {
callback();
}, 1000);
}
c(() => {
console.log('1000ms 후에 callback 함수가 실행된다.');
});
c(() => {
c(() => {
console.log('2000ms 후에 callback 함수가 실행된다.');
});
});
promise를 활용한 비동기작업
then 함수에서 다시 프로미스 객체를 리턴하는 방법을 통해 체이닝하면, 비동기 작업을 순차적으로 아래로 표현할 수 있다. → then에 함수를 넣는 여러가지 방법
*arrow function에서 return을 바로 해줄 경우: 중괄호와 return을 생략하고 작성할 수 있다.
function p() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
});
};
p().then(() => { // p() 실행 1초뒤에 .then() ~ return p()
return p();
})
.then(() => p()) // 2초 뒤에 .then(() => p())
.then(p) // 3초뒤 실행
.then(() => { // 4초뒤 실행
console.log('4000ms 후에 fulfilled 된다.');
})
value의 프로미스 객체 여부와 실행방법
앞에서는 promise 객체를 만들 때 생성자 함수를 이용해서 new Promise에 executor를 넣어주어 만드는 방법을 사용하였다.
promise 객체를 만드는 방법은 여러가지가 있는데 여기서는 Promise.resolve( ) 를 이용할 것이다.
Promise라는 전역 객체 안에 있는 resolve 함수를 실행하면서 promise를 만들어내는 방법이다.
resolve 함수 내부에 인자로 value 값이 들어갈 수 있다.
value 값으로 들어갈 수 있는 것은 크게 두 가지가 있다.
- Promise 객체
- Promise 객체가 아닌 값
value 가 프로미스 객체인지 아닌지 알 수 없는 경우, 사용하면 연결된 then 메서드를 실행한다.
- value가 프로미스 객체면, resolve 된 then 메서드를 실행한다.
- value가 프로미스 객체가 아니면, value를 인자로 보내면서 then 메서드를 실행한다.
Promise.resolve(/* value */);
// 1. resolve의 인자 value로 비동기 promise 객체
Promise.resolve(new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 1000);
})).then((data) => {
console.log('프로미스 객체인 경우, resolve 된 결과를 받아 then이 실행된다.');
})
// 2. resolve의 인자 value로 값
Promise.resolve('bar').then(data => {
console.log('then 메서드가 없는 경우, fulfilled 된다.', data);
})
// 전체 출력
// then은 promise 객체가 resolve 된 후에 호출된다.
// 프로미스 객체인 경우, resolve 된 결과를 받아 then이 실행된다.
// (프로미스 객체가 들어있는 경우 1초 뒤에 실행되었기 때문에 뒤에 출력되었다.)
reject 함수와 value
Promise.reject를 사용하면, catch로 연결된 rejected 상태로 변경된다.
Promise.reject(/* value */);
Promise.reject(new Error('reason')).then(error => {
}).catch(error => {
console.log(error);
});
Promise 객체 다중생성과 실행방법1 - Promise.all
프로미스 객체 여러개를 생성하여, 배열로 만들어 인자로 넣고 Promise.all 을 실행하면 -- Promise.all([프로미스 객체들])
배열의 모든 프로미스 객체들이 fulfilled 되었을 때, then의 함수가 실행된다.
then의 함수의 인자로 프로미스 객체들의 resolve 인자값을 배열로 돌려준다.
function p(ms) { // *ms: milliseconds의 숫자를 인자로 받음
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(ms);
}, ms);
})
};
// p 함수로 만들어낸 프로미스 객체 여러개 생성 - Promise.all()에 배열로 인자를 삽입
// p 함수로 만들어낸 프로미스 객체의 인자는 1000ms, 2000ms, 3000ms
Promise.all([p(1000), p(2000), p(3000)]).then((messages) => {
console.log('모두 fulfilled 된 이후에 실행된다.', messages);
})
// 출력: 모두 fulfilled 된 이후에 실행된다. [ 1000, 2000, 3000 ]
// 배열의 모든 프로미스 객체들이 fulfilled 이행되었을 때 then의 함수가 실행되므로
// 3초 후에 then() 이 실행된다.
Promise 객체 다중생성과 실행방법2 - Promise.race
프로미스 객체 여러개를 생성하여 배열로 만들어 인자로 넣고 Promise.race를 실행하면 - Promise.race([프로미스 객체들])
배열의 모든 프로미스 객체들 중 가장 먼저 fulfilled 된 것으로, then의 함수가 실행된다.
function p(ms) { // *ms: milliseconds의 숫자를 인자로 받음
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(ms);
}, ms);
})
};
// p 함수로 만들어낸 프로미스 객체 여러개 생성 - Promise.all()에 배열로 인자를 삽입
// p 함수로 만들어낸 프로미스 객체의 인자는 1000ms, 2000ms, 3000ms
Promise.race([p(1000), p(2000), p(3000)]).then((message) => {
console.log('가장 빠른 하나가 fulfilled 된 이후에 실행된다.', message);
})
// 출력: 가장 빠른 하나가 fulfilled 된 이후에 실행된다. 1000
// 배열의 모든 프로미스 객체들 중 가장 먼저 fulfilled 된 것으로 then의 함수가 실행되므로
// 1초 후에 then() 이 실행된다.