하루에 한 문제
Promise 본문
Promise란?
Promise는 자바스크립트 비동기 처리에 사용되는 객체다.
Promise의 3가지 상태(states)
- Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
- Fulfilled(이행) : 비동기 처리가 완료되어 Promise가 결과 값을 반환해준 상태
- Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
Pending(대기)
먼저 아래와 같이 new Promise()메서드를 호출하면 대기(Pending)상태가 된다.
new Promise();
- Promise의 매개변수는 resolve, reject라고 하는 콜백함수를 가진다.
- 또한 반환 결과는 Promise이다!
new Promise(function(resolve, reject) {
// ...
});
Fulfilled(이행)
new Promise(function(resolve, reject) {
resolve();
});
그리고 이행 상태가되면 아래와 같이 then() 을 이용해 resolve에서 넘겨준 매개변수를 받아서 사용할 수 있다.
function getData() {
return new Promise(function(resolve, reject) {
var data = 100;
resolve(data);
});
}
// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
console.log(resolvedData); // 100
});
- then은 다시 Promise를 반환한다!
- 그렇기 때문에 then(~~).catch(~~).finally(~~) 와 같은 체이닝이 가능한 것이다!
Rejected(실패)
new Promise()로 Promise 객체를 생성하면 콜백 함수 인자로 resolve와 reject를 사용할 수 있다고 했는데. 여기서 reject를 아래와 같이 호출하면 실패(Rejected) 상태가 된다.
new Promise(function(resolve, reject) {
reject();
});
- 실패 상태가 되면 실패한 이유를 catch()를 이용해 받을 수 있다.
- 역시 catch또한 다시 Promise를 반환한다.
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 Chaining
function getData() {
return new Promise({
// ...
});
}
// then() 으로 여러 개의 Promise를 연결한 형식
getData()
.then(function(data) {
// ...
})
.then(function() {
// ...
})
.then(function() {
// ...
});
위에서 말했듯이 then, catch등의 return 값이 Promise이기 때문에 이러한 체이닝이 가능하다.
실무에서 있을 법한 연결 사례
getData(userInfo)
.then(parseValue)
.then(auth)
.then(diaplay);
- 위 코드는 페이지에 입력된 사용자 정보를 받아와 파싱, 인증 등의 작업을 거치는 코드를 나타낸다.
- 아래의 코드와 같이 userInfo는 사용자 정보가 담긴 객체를 의미하고, parseValue, auth, display는 각각 Promise를 반환해주는 함수이다.
var userInfo = {
id: 'test@abc.com',
pw: '****'
};
function parseValue() {
return new Promise({
// ...
});
}
function auth() {
return new Promise({
// ...
});
}
function display() {
return new Promise({
// ...
});
}
Promise 에러 처리 방법
에러 처리 방법에는 다음과 같이 2가지 방법이 있습니다.
1.then()의 두 번째 인자로 에러를 처리하는 방법
getData().then(
handleSuccess,
handleError
);
2.catch()를 이용하는 방법
getData().then().catch();
- 위 2가지 방법 모두 Promise의 reject() 메서드가 호출되어 실패 상태가 된 경우에 된다.
- 즉 Promise의 로직이 정상적으로 돌아가지 않는 경우 호출된다.
function getData() {
return new Promise(function(resolve, reject) {
reject('failed');
});
}
// 1. then()의 두 번째 인자로 에러를 처리하는 코드
getData().then(function() {
// ...
}, function(err) {
console.log(err);
});
// 2. catch()로 에러를 처리하는 코드
getData().then().catch(function(err) {
console.log(err);
});
하지만 Promise의 에러 처리에는 가급적 catch()를 사용하는 것이 좋다.
// then()의 두 번째 인자로는 감지하지 못하는 오류
function getData() {
return new Promise(function(resolve, reject) {
resolve('hi');
});
}
getData().then(function(result) {
console.log(result);
throw new Error("Error in then()"); // Uncaught (in promise) Error: Error in then()
}, function(err) {
console.log('then error : ', err);
});
getData() 함수의 Promise에서 resolve() 메서드를 호출하여 정상적으로 로직을 처리했지만, then()의 첫 번째 콜백 함수 내부에서 오류가 나는 경우 오류를 제대로 못 잡아 낸다.
하지만 똑같은 오류를 catch()로 처리하면 오류를 잡아낼 수 있다.
// catch()로 오류를 감지하는 코드
function getData() {
return new Promise(function(resolve, reject) {
resolve('hi');
});
}
getData().then(function(result) {
console.log(result); // hi
throw new Error("Error in then()");
}).catch(function(err) {
console.log('then error : ', err); // then error : Error: Error in then()
});
자 이제 예를 통해 보는것이 아닌 직접 만들어서 확인해보자!
function delay(sec, callback) {
setTimeout(() => {
callback(new Date().toISOString());
}, sec * 1000);
}
console.log('start');
delay(1, (result) => console.log(result));
console.log('end');
- 이러한 delay함수가 있다고 치자.
- 이 delay함수는 매개변수로 sec, callback을 받는다
- setTImeout으로 sec초 후에 callback함수를 실행한다.
- 위 코드의 실행결과는 어떻게 될까?
- setTimeout은 비동기 함수이기 때문에 아래와 같이 나온다.
function delay(sec, callback) {
setTimeout(() => {
callback(new Date().toISOString());
}, sec * 1000);
}
console.log('start');
delay(1, (result) => console.log(result));
delay(1, (result) => console.log(result));
delay(1, (result) => console.log(result));
console.log('end');
- 위의 코드는 콘솔에 출력해보면 비동기이기 때문에 3개의 result가 거의 동시에 찍히게 된다.
- 만약의 위의 코드를 통해 시작 시간으로부터 1초, 2초, 3초 뒤에 result를 출력하고 싶다면 어떻게 하면 될까?
console.log('start');
delay(1, (result) => {
console.log(result);
delay(1, (result) => {
console.log(result);
delay(1, (result) => {
console.log(result);
});
});
});
console.log('end');
- 위의 코드와 같이 콜백함수 안에 콜백함수를 넣어주면 된다.
- 그런데 이러한 코드는 소위 말하는 콜백지옥을 유발할 수 있기 때문에 좋은 코드는 아니다.
위의 코드를 Promise를 사용해 나타내보자.
function delayP(sec) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new Date().toISOString());
}, sec * 1000);
});
}
console.log('start');
delayP(1)
.then((res) => {
console.log(res);
return delayP(1);
})
.then((res) => {
console.log(res);
return delayP(1);
})
.then((res) => {
console.log(res);
return delayP(1);
});
console.log('end');
- 우선 결과를 보면 우리가 원했던 것처럼 나온다!
- delayP함수에서는 return으로 다시 delayP()를 넘겨주기 때문에 계속해서 하나의 비동기처리가 끝나면 다음 비동기가 처리되는 모습을 볼 수 있다.
참고
www.youtube.com/watch?v=CA5EDD4Hjz4
'Dev > JavaScript' 카테고리의 다른 글
V8 작동원리 (0) | 2021.04.20 |
---|---|
Arrow Function에서 this바인딩 (0) | 2021.04.20 |
async VS defer (0) | 2021.04.17 |
NPM 이란? (1) | 2021.04.16 |
클로저 (0) | 2021.04.15 |
Comments