Promise 매커니즘
( 1 )new Promise(function(){})
new
연산자로 Promise 를 호출하면, Promise.prototype.constructor 를 호출하여 Promise 객체를 만든다.- 일반적으로
new Point(one,two)
처럼 인스턴스의 초깃값을 생성자에게 넘겨주지만, Promise 는 비동기 처리를 위한 환경을 만들어 주는 것을 목적으로 고안되어, 파라미터에 함수를 넘겨준다. - 생성자에게 실행자를 파라미터로 넘겨주는 것은 확장성을 고려한 것이다. 실행자 함수는 생성자(constructor)가 확장된 개념으로, JS에서 정해진 생성자의 로직을 따르는게 아니라, 개발자가 정한 실행자의 로직으로 객체를 생성할 수 있게 해준다.
( 2 ) 실행자의 파라미터에 함수 "이름"을 넘긴다.
- Promise에 인자로 들어가는 실행자는 함수 이름을 파라미터로 받는다.
new Promise((resolve, reject) => { })
에서(resolve, reject) => {}
가 실행자이며, 인자로 resolve와 reject라는 함수 이름을 파라미터로 받는다.
- 엔진은 실행자의 파라미터를 식별자로 하는 function Object를 생성한다. 즉, resolve와 reject라는 function Object를 생성한다.
new Promise((resolve, reject) => { resolve("성공"); reject("실패"); });
- 실행자의 파라미터가 Function Object이므로 호출이 가능하다.
- 우리가 따로 resolve와 reject에 대한 함수를 정의하지 않았어도, 오류가 나지 않는 것은 엔진이 실행자의 파라미터를 보고 function Object를 만들었기 때문이다.
- resolve와 reject는 엔진이 만든 함수다. 따라서, resolve와 reject는 호출되면, 파라미터를 then에 맵핑하는 것 이외에, 엔진이 지정한 부가적인 처리를 할 수 있게 되었다.
- 주의할 점은, 실행자의 파라미터로 들어가는 함수를 익명함수로 하면, 에러가 발생하진 않지만,
then()
이 호출될 수 없다.
( 3 ) 파라미터 순서에 의한 성공/실패 함수 구분
var obj = new Promise((one, two) => {
one();
});
obj.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- 엔진은 Promise가 성공/실패 했을 때, 호출할 함수를 실행자 파라미터 순서로 구분한다.
- Promise 상태가 fullfiled면, 실행자의 첫 번째 파라미터를 호출한다.
- Promise 상태가 rejected면, 실행자의 두 번째 파라미터를 호출한다.
- 순서로 구분하기 때문에, 실행자의 성공, 실패 여부 함수의 이름을 임의로 작성할 수 있다. 그러나 성공함수의 이름은
resolve
로, 실패 함수의 이름은reject
로 하는 것이 시멘틱이다.
- 순서로 구분하기 때문에, 실행자의 성공, 실패 여부 함수의 이름을 임의로 작성할 수 있다. 그러나 성공함수의 이름은
- 엔진이 만든 성공/실패(resolve/reject) function Object는 추후, then 호출시에 사용되기 위해 어딘가에 저장해둬야 한다. 엔진은 성공/실패 function Object를 Promise 인스턴스의 내부 프로퍼티
[[PromiseStatus]]
에 저장해둔다.
( 4 ) Promise 상태 결정의 순간
지금껏, Promise 상태가 fullfilled나 rejected로 변하면, 그때 성공/실패 함수를 호출한다고 정리했다. 그러나 이것은 사실이 아니다. 인과관계가 잘못되었다.
사실은, 실행자에서 resolve가 먼저 호출되면 Promise의 상태가
fullfilled
가 되고, reject가 먼저 호출되면rejected
상태가 되는 것이다. 즉, resolve/reject에 의해 Promise의 상태가 결정되는 것이다.
new Promise((resolve, reject) => {
reject("실패");
resolve("성공");
}).then(
(value) => console.log(value),
(reason) => console.log(reason)
); //=> 실패
new Promise((resolve, reject) => {
resolve("성공");
reject("실패");
}).then(
(value) => console.log(value),
(reason) => console.log(reason)
); //=> 성공
- resolve를 먼저 호출하면, Promise 인스턴스의 내부 프로퍼티
[[PromiseStatus]]
는 fullfilled가 된다. - reject를 먼저 호출하면,
[[PromiseStatus]]
는 rejected가 된다.
따라서, 성공과 실패 여부는 실행자 안의 로직에서 결정해야 된다는 것이다. 분기문으로 Promise가 성공했다고 판단되었을 때 resolve를 호출하고, Promise가 실패했다고 판단되는 경우엔 reject를 호출하는 로직을 직접 작성해야 된다.
Promise는 개발자가 비동기 처리를 원하는 코드를 작성할 때 쓰는 것이다. 엔진이 알아서 해주는 것은 function Object 생성과, 개발자가 정한로직에 의해 성공/실패 상태를 결정하고 성공/실패 함수를 호출해주는 것 뿐이다.
( 5 ) 인스턴스에 저장과 사용
then()
의 핸들러 함수에서 사용할 값들을 Promise 인스턴스에 저장한다.
new Promise((resolve, reject) => {
resolve("성공");
}).then(
(value) => console.log(value),
(reason) => console.log(reason)
);
- resolve가 호출되면
"성공"
을 Promise의[[PromiseValue]]
에 저장한다. then()
이 실행될 때, 엔진은 먼저[[PromiseStatus]]
를 살펴본다.- 값이 fullfilled면
then()
의 첫 번째 파라미터(핸들러 함수)를 호출한다. - 값이 rejected면
then()
의 두 번째 파라미터(핸들러 함수)를 호출한다.
- 값이 fullfilled면
- 핸들러 함수가 호출되면서 동시에
[[PromiseValue]]
의 값과 핸들러의 파라미터를 맵핑한다.
( 6 ) Promise 객체의 반환
new Promise((resolve) => {
resolve(100);
})
.then((value) => {
console.log(value);
return value + value;
})
.then((value) => {
console.log(value);
});
then()
과catch()
는 Promise 객체를 받아, Promise 객체를 반환한다.- 따라서
then()
과catch()
로 체이닝이 가능한 것이다.
- 따라서
- 첫
then()
은 비동기로 실행되나, 이어지는 처리는 동기/비동기 모두 가능하다. 현재의 then에서 비동기처리를 안해주면 이어지는 then은 동기적으로 실행된다.return new Promise
를 하게되면 비동기적으로 이어지는 then이 실행된다.