Dev./javascript
[JS] Promise의 이해
인쥭
2021. 6. 10. 11:07
반응형
1. Promise 코드 예시
promise[명사] 약속
- 단어의 의미로 보아, javascript와 무엇인가 약속을 하는 것으로 짐작해볼 수 있다.
- 우선 다음의 코드 블록을 보자.
function getMessage() {
return "hello monkey";
}
console.log("start");
console.log(getMessage());
console.log("end");
- 절차적으로 실행되는 javascript의 특성상 start, hello monkey, end가 순서대로 console에 출력될 것을 어렵지 않게 짐작할 수 있다.
- getMessage 함수가 Promise 인스턴스를 반환하도록 다음과 같이 수정하여 실행해보자.
function getMessage() {
return new Promise((resolve) => {
resolve("hello monkey");
console.log("I'm in Promise!!!");
});
}
console.log("start");
const youPromisedMe = getMessage();
youPromisedMe.then((message) => {
console.log(message);
});
console.log("end");
- Promise를 이해하지 못했더라도, 지금은 우선 단순한 console.log 작성 순서에 집중하고 실행 결과를 보자.
- Promise 오브젝트 내부의 코드는 비동기적으로 실행되었다. 위 코드를 한줄씩 생각해보면 다음과 같다.
/*
getMessage 함수를 실행하면 Promise 인스턴스를 반환한다.
반환되는 Promise 인스턴스는 항상 성공하며,
resolve를 통해 "hello monkey" 값을 then의 핸들러 함수에 전달하게 될 것이다.
또한, Promise 인스턴스의 매개변수로 전달된 함수에서는 console.log도 실행하고 있다.
*/
function getMessage() {
return new Promise((resolve) => {
resolve("hello monkey");
console.log("I'm in Promise!!!");
});
}
// start를 콘솔에 출력한다.
console.log("start");
/*
미리 선언된 getMessage 함수를 호출하므로,
youPromisedMe 변수에 Promise 인스턴스를 할당한다.
*/
const youPromisedMe = getMessage();
/*
youPromisedMe라는 Promise가 성공했을 때,
resolve에 의해 전달된 값을 message로 받아 콘솔에 출력한다.
*/
youPromisedMe.then((message) => {
console.log(message);
});
// end를 콘솔에 출력한다.
console.log("end");
- 이렇게만 보면 실행 순서는 start > I'm in Promise!!! > hello monkey > end가 되어야할 것 같은데, 실제로는 hello monkey가 마지막에 출력된다.
- 즉, then()의 내용이 절차적으로 실행되지 않았다!
- 이로 미루어 Promise는 어떤 조건이 만족되었을 때 then()의 내용을 실행할 수 있도록 약속하는 객체로 짐작해볼 수 있다.
- 이 시점에서, 몇 가지의 Promise 특징을 짚어보자.
2. Promise의 작성
- Promise는 javascript에서 비동기 작업에 사용되는 객체이다.
- 비동기적으로 실행될 작업을 Promise의 매개변수에 다음과 같은 형식으로 넣어준다.
new Promise( (resolve, reject) => {
/*
비동기 작업 성공시 resolve()를 호출하고,
비동기 작업 실패시 reject()를 호출하도록 구현한다.
*/
});
- Promise 인스턴스의 매개변수로 전달되는 함수의 매개변수는 반드시 resolve, reject일 필요는 없다. 짝만 맞춰주면 된다!
function getMessage(succeed) {
return new Promise((code, monkey) => {
if(succeed) {
code("성공!");
} else {
monkey("실패...");
}
});
}
const weirdPromise = getMessage(true);
weirdPromise.then(
(message) => { console.log(message) }
);
/* 실행 결과
성공!
*/
- 그러나 시맨틱상 resolve, reject 등 의미가 명확히 전달되는 명칭을 사용하는 것이 권장된다.
- resolve와 reject는 하술할 것.
3. Promise의 상태
- Promise는 세 개중 하나의 상태를 갖는다.
- pending(대기): Promise가 생성되어 작업을 진행 중인 상태
- fulfilled(이행): Promise가 작업을 성공적으로 완료한 상태
- rejected(거절됨): Promise가 작업을 완료하였지만 실패한 상태
- 완료 상태인 fulfilled와 rejected를 합쳐 settled라고도 한다고 함.
- 이러한 상태 정보는 [[PromiseState]]에 명시된다.
- 위 내용은 다음과 같다.
- prom1: 아무 작업을 수행하지 않는 Promise를 생성하였으므로, 상태는 pending
- prom2: resolve가 즉시 실행되는 Promise이므로, 상태가 fulfilled로 변경됨
- prom3: reject가 즉시 실행되는 Promise이므로, 상태가 rejected로 변경됨
- 이렇듯, resolve와 reject는 각각 Promise의 상태를 변경하기 위해 사용하는 것을 알 수 있다.
- 때문에 1.의 코드블록에서 hello monkey가 뒤늦게 실행되었다.
- new Promise() 내부에 작성한 resolve는 이행 상태로 변경할 때 어떤 파라미터를 넘겨줄지 명시한 것이며,
실제로 이행 상태인 Promise를 생성한 것이 아니기 때문이다.
- new Promise() 내부에 작성한 resolve는 이행 상태로 변경할 때 어떤 파라미터를 넘겨줄지 명시한 것이며,
4. Promise의 메소드
- Promise와 관련된 메소드를 확인하기 위해 new Promise(()=>{});와 같이, 아무런 작업도 하지 않는 Promise를 객체에 할당하자.
- obj.__proto__에는 Promise로부터 상속받은 메소드들이 설정된다.
- 이 중, Promise의 작업 완료시 [[PromiseState]]에 따라 동작하는 then()과 catch()를 살펴보자.
- then()은 다음과 같은 형식으로 사용할 수 있다.
- Promise.then(함수1, 함수2)
- 함수1은 Promise가 resolve되었을 때 resolve의 매개변수로 주어진 값을 받아 처리하는 성공 핸들러 함수이다.
- (Optional) 함수2는 Promise가 reject되었을 때, reject의 매개변수로 주어진 값을 받아 처리하는 실패 핸들러 함수이다.
- Optional한 이유는 catch()에서 하술
function getMessage(succeed) {
return new Promise((resolve, reject) => {
if(succeed) {
resolve("성공!");
} else {
reject("실패...");
}
});
}
function successHandler(message) {
console.log(`성공했을 경우 ${message}이 출력`);
}
function failHandler(message) {
console.log(`실패했을 경우 ${message}가 출력`);
}
const succeedPromise = getMessage(true);
succeedPromise.then(successHandler, failHandler);
const failedPromise = getMessage(false);
failedPromise.then(successHandler, failHandler);
/* 실행 결과
성공했을 경우 성공!이 출력
실패했을 경우 실패...가 출력
*/
- catch()는 Promise.catch(함수) 형식으로 사용한다.
- then()의 함수2가 optional한 이유는 catch()의 존재가 있기 때문이다.
- Promise.then(함수1, 함수2)를 Promise.then(함수1).catch(함수2) 형태로 사용할 수 있다.
function getMessage(succeed) {
return new Promise((resolve, reject) => {
if(succeed) {
resolve("성공!");
} else {
reject("실패...");
}
});
}
function successHandler(message) {
console.log(`성공했을 경우 ${message}이 출력`);
}
function failHandler(message) {
console.log(`실패했을 경우 ${message}가 출력`);
}
const succeedPromise = getMessage(true);
succeedPromise
.then(successHandler)
.catch(failHandler);
const failedPromise = getMessage(false);
failedPromise
.then(successHandler)
.catch(failHandler);
/* 실행 결과
성공했을 경우 성공!이 출력
실패했을 경우 실패...가 출력
*/
- Promise.then(함수1, 함수2)는 성공 핸들러 함수인 함수1의 에러를 함수2에서 처리할 수 없다. 따라서 별도의 로직이 필요하다.
- Promise.then(함수1).catch(함수2)는 성공 핸들러 함수인 함수1의 에러를 함수2에서 처리할 수 있다!
- 이는 then(), catch()의 반환값이 새로운 Promise 객체이기 때문이다.
function getMessage(succeed) {
return new Promise((resolve, reject) => {
if(succeed) {
resolve("성공!");
} else {
reject("실패...");
}
});
}
function successHandler(message) {
console.log(`성공했을 경우 ${message}이 출력`);
throw new Error();
}
function failHandler(message) {
console.log(`실패했을 경우 ${message}가 출력`);
}
const catchPromise = getMessage(true);
catchPromise
.then(successHandler)
.catch(failHandler);
/* 실행 결과
성공했을 경우 성공!이 출력
실패했을 경우 Error가 출력
// successHandler에서 실행된 throw new Error()를 처리하였음!
*/
// const onlyThenPromise = getMessage(true);
// onlyThenPromise.then(successHandler, failHandler);
/* 실행 결과
성공했을 경우 성공!이 출력
// successHandler에서 실행된 throw new Error()를 처리하지 못했음!
*/
- then()과 catch()가 반환하는 것은 새로운 Promise 객체이므로 method chaining이 가능하다.
function getMessage(succeed) {
return new Promise((resolve, reject) => {
if(succeed) {
resolve("성공!");
} else {
reject("실패...");
}
});
}
function successHandler(message) {
console.log(`성공했을 경우 ${message}이 출력`);
return "code monkey";
}
function failHandler(message) {
console.log(`실패했을 경우 ${message}가 출력`);
}
const monkeyPromise = getMessage(true);
monkeyPromise
.then(successHandler)
.then(successHandler)
.catch(failHandler);
/* 실행 결과
성공했을 경우 성공!이 출력
성공했을 경우 code monkey이 출력
*/
- successHandler가 return하는 문자열은 then 메소드가 받아 새로운 Promise 인스턴스를 생성하여 다음 메소드로 넘겨주고 있다.