서두
단어에 해당하는 번역에 주의하여 전체 패턴 이해에 도움이 되도록 합니다
-
promise: 약속
-
pending:待定
-
settled:定了
-
resolve(d): (已)해결
-
reject(ed): (已)거부
-
onFulfilled: 이행됨 (이벤트)
-
onRejected: 거부됨 (이벤트)
-
then: 그 다음, 이어서
-
catch: 캡처
-
race: 경주
-
future: 미래 (값)
-
thenable: then 가능 (then 속성 보유)
一.Promise 란 무엇인가
비동기 작업 처리 메커니즘의 한 세트, Promise A+ 는 규격이며, jQuery, ES2015, q 의 promise 는 모두 이 규격의 한 구현입니다
二.Promise 의 특징
-
비동기 작업의 실행 순서를 더 쉽게 제어 가능
-
일련의 비동기 작업에서 오류를 유연하게 처리 가능
-
체인 호출을 지원하며, 작성하기 좋음
-
콜백 피라미드를 제거할 수 있으며, 보기 좋음
三.Promise 의 용도
-
직렬 작업 큐 구현
-
직렬 작업 파이프라인 구현
-
런타임에야 실행 순서가 결정되는 직렬 작업 처리
(분량 제한으로, 구체적인 적용 장면은 나중에 전개)
四.Promise 타입
내부 속성 [[PromiseStatus]]/[[PromiseValue]]
####[[PromiseStatus]]: pending(待定) | resolved(已해결) | rejected(已거부)
뒤의 두 개를 총칭하여 settled(定了), pending 은 초기 상태로, new Promise(func) 후, func 중의 resolve 와 reject 가 아직 실행되지 않은 경우, 이때 status=pending
####[[PromiseValue]]: undefined | resolve/reject 의 매개변수 | Promise.resolve/reject 의!thenable 매개변수 | onFulfilled/onRejected 의 반환값
then 이 호출될 때, 이 값은 then 의 onFulfilled 또는 onRejected 에 전달되며, resolve 와 reject 가始終 실행되지 않으면, then 의 onFulfilled 와 onRejected 도始終 실행되지 않습니다
생성자 매개변수 resolve/reject
####resolve(해결)
resolve 실행 후, then 의 onFulfilled 콜백을 트리거하고, resolve 의 매개변수를 onFulfilled 에 전달합니다
####reject(거부)
reject 실행 후, then | catch 의 onRejected 콜백을 트리거하고, reject 의 매개변수를 onRejected 에 전달합니다
주의:바로 뒤의 then 이 onRejected 콜백을 선언하지 않은 경우, 다음 then | catch 의 onRejected 에 전달되고, 아직 없으면, 계속 전달...끝까지 없으면, 오류 Uncaught (in promise) Error:xxx 보고
特殊的:resolve 의 매개변수가 Promise 객체인 경우, 해당 객체의 최종 [[PromiseValue]] 는 외층 Promise 객체 후속의 then 의 onFulfilled/onRejected 에 전달됩니다. 예를 들어:
// 외층 then 은 내층 future 와 error 를 얻을 수 있음
new Promise(function(resolve, reject) {
resolve(new Promise(function(resolve, reject) {
resolve(12);
// reject(new Error('reject in nested Promise'));
}).then(function(future) {
return future + 1;
}, function(error) {
return new Error('reject again in nested Promise');
}));
}).then(onFulfilled, onRejected);
// => 13 onFulfilled 이 내층 promise 의 최종적인 [[PromiseValue]] 를 받았기 때문
// reject 인 경우, onFulfilled 은 Error('reject again') 를 받음
五.기본 문법
###1.Promise 객체 생성
Promise 를 생성하는 기본 문법은 다음과 같습니다:
// 생성
var promise = new Promise(function(resolve, reject) {
if (/*...*/) {
resolve(data);
}
else {
reject(new Error('error occurs'));
}
});
// 사용
// then
promise.then(function(future) {
// ...
}, function(error) {
// ...
});
// catch
promise.catch(function(error) {
// ...
});
주의:new Promise(func) 시, func 는즉시 실행됩니다 (generator 가 선언만 하고 실행하지 않는 것과 다름), 실행 과정 중 resolve() 를 만나도 못 본 척 (P.S.간단히 못 본 척으로 이해, 설명은 주석 참조), reject() 를 만나면 즉시 오류를 스로우 (주의:Chrome46 은 즉시 오류 스로우, FF39 는 오류 없음, 其它 polyfill, 예:es6-promise、promise-polyfill 는 오류 없음), 대비 예는 다음과 같습니다:
new Promise(function(resolve, reject) {
console.log('#1');
resolve(1);
console.log('#2');
});
// => #1 #2 오류 없음 (resolve 를 만나도 못 본 척)
// 실제로 내부 속성 [[PromiseStatus]] 과 [[PromiseValue]] 는 변했지만, then 이 제공하는 onFulfilled 콜백이 없어 효과를 볼 수 없음
new Promise(function(resolve, reject) {
console.log('#1');
reject(new Error('reject'));
console.log('#2');
});
// => #1 #2 Chrome46 오류 보고:Uncaught (in promise) Error: reject(…)
// 뒤에 then/catch 가 제공하는 onRejected 콜백이 없어 예외가 소비되지 않음
P.S.reject 의 매개변수는 임의의 타입이어도 되지만, 일반적으로 오류 원인을 나타내는 Error 객체를 전달함
###2.Promise.prototype.then(onFulfilled, onRejected) 그 다음
promise 에 onFulfilled/onRejected 콜백 handler 를 추가하고, promise 의 resolve/reject 가 실행될 때 대응하는 handler 를 트리거
주의:promise.then 으로 콜백 함수를 추가할 때, promise 가 이미 fulfilled 또는 rejected 상태인 경우, 해당 메서드는즉시 호출됩니다
Promise 객체를 반환 (체인 호출 가능), 간단히 다음과 같이 이해:
1.onFulfilled 이 트리거된 경우, 해당 객체의 [[PromiseValue]]=onFulfilled 의 반환값, [[PromiseStatus]]=resolved
new Promise(function(resolve, reject) {
resolve(2);
}).then(function(future) {
return future * 2;
}, onRejected).then(onFulfilled, onRejected);
// onFulfilled 이 4 를 얻음
2. onRejected 이 트리거된 경우, 해당 객체의 [[PromiseValue]]=onRejected 의 반환값, [[PromiseStatus]]=resolved
특히 주의: 반환 객체의 상태는 resolved 이며, rejected가 아님
new Promise(function(resolve, reject) {
reject(2);
}).then(null, function(err) {
return err * 2;
}).then(onFulfilled, onRejected);
// 여전히 onFulfilled 이 4 를 얻음, onRejected 이 아님
P.S.실제로는 [[PromiseValue]]=undefined, [[PromiseStatus]]=pending, 하지만 여전히 위와 같은 상태로 실행
###3.Promise.prototype.catch(onRejected) 캡처
then(null, onRejected) 과 동등하며, Promise 객체를 반환하고, 간단히 다음과 같이 이해:
-
onReject 이 트리거된 경우, 반환 객체의 [[PromiseValue]]=onReject 의 반환값, [[PromiseStatus]]=resolved
-
onReject 이始終 트리거되지 않은 경우, catch 를 못 본 척하고, 직접 원래 Promise 객체를 반환
var p = new Promise(function(resolve, reject) { resolve(2); }).catch(function() { console.log('onRejected'); return false; }); p.then(onFulfilled, onRejected); // onFulfilled 이 2 를 얻음, catch 는 아무 영향도 없음
P.S.실제로는 [[PromiseValue]]=undefined, [[PromiseStatus]]=pending, 하지만 여전히 위와 같은 상태로 실행
###4.Promise.all(iterable)
Promise 객체를 반환하고, iterable 중의 모든 promise 가 resolve 된 후, onFulfilled 을 트리거하고, 첫 번째 부정 promise 를 만나면 즉시 종료하고, onRejected 을 트리거
//1.전肯定 집합, onFulfilled 을 트리거, 전달되는 매개변수는 집합 중의 모든 promise 의 [[PromiseValue]] 로 구성된 배열
Promise.all([getPromise(), getPromise()]).then(onFulfilled, onRejected);
//2.전 부정 집합, 첫 번째 부정 promise 를 만나면 종료, onRejected 을 트리거, 전달되는 매개변수는 해당 promise 의 [[PromiseValue]]
// Promise.all([getPromise(true), getPromise(true)]).then(onFulfilled, onRejected);
// 3.일반 집합, 위와 같음
// Promise.all([getPromise(true), getPromise()]).then(onFulfilled, onRejected);
P.S.all, 모두 정상적으로 완료되거나, 오류로 중단되며, 모두 멈춤
###5.Promise.race(iterable) 경주
Promise 객체를 반환하고, iterable 중 임의의 하나의 promise 가 resolve 되면 즉시 종료하고, onFulfilled 을 트리거하고, 부정 promise 를 만나면 즉시 종료하고, onRejected 을 트리거
// 1.전肯定 집합, 첫 번째肯定 promise 를 만나면 종료, onFulfilled 을 트리거, 전달되는 매개변수는 해당 promise 의 [[PromiseValue]]
// Promise.race([getPromise(), getPromise()]).then(onFulfilled, onRejected);
// 2.전 부정 집합, 첫 번째 부정 promise 를 만나면 종료, onRejected 을 트리거, 전달되는 매개변수는 해당 promise 의 [[PromiseValue]]
// Promise.race([getPromise(true), getPromise(true)]).then(onFulfilled, onRejected);
// 3.일반 집합, 위와 같음
// Promise.race([getPromise(true), getPromise()]).then(onFulfilled, onRejected);
P.S."경주", 결과가肯定인지 부정인지에 관계없이, 가장 빠른 것만
###6.Promise.resolve(value) 해결
1.value 가 promise 인 경우, promise 중의 resolve/reject 의 매개변수를 대응하는 onFulfilled/onRejected 에 전달
Promise.resolve(getPromise()).then(onFulfilled, onRejected);
// onFulfilled 이 resolve 의 매개변수를 얻음
Promise.resolve(getPromise(true)).then(onFulfilled, onRejected);
// onRejected 이 reject 의 매개변수를 얻음
2.value 가 thenable 객체 (then 속성을 가진 객체) 인 경우, obj 를 Promise 객체로 래핑하고, 해당 객체의 then 은 obj.then
Promise.resolve({
then: function(onFulfilled, onRejected) {
// onFulfilled(1);
onRejected(new Error('obj.then err'));
console.log('obj.then');
}
}).then(onFulfilled, onRejected);
3.value 가!thenable 값인 경우, 해당 값을 Promise 객체로 래핑하고, 해당 객체의 [[PromiseValue]]=value, [[PromiseStatus]]=resolved, 따라서 then 메서드를 호출할 때, onFulfilled 은 이 값을 받음
Promise.resolve(1).then(onFulfilled, onRejected);
// onFulfilled 이 1 을 얻음
###7.Promise.reject(reason) 거부
reason 을 Promise 객체로 래핑하고, 해당 객체의 [[PromiseValue]]=reason, [[PromiseStatus]]=rejected, 따라서 then 메서드를 호출할 때, onRejected 은 이 값을 받음
주의: 여기에는 thenable/!thenable 의 구분이 없으며, 일률적으로 처리:
// 1.!thenable onRejected 은 Error 객체를 얻음
Promise.reject(new Error('static reject')).then(onFulfilled, onRejected);
// 2.promise onRejected 은 감싸진肯定 Promise 객체를 얻음
// 해당 객체의 [[PromiseValue]]=promise, [[PromiseStatus]]=rejected
Promise.reject(getPromise()).then(onFulfilled, onRejected);
// 3.thenable onRejected 은 {then: xxx} 를 얻음
Promise.reject({
then: function(onFulfilled, onRejected) {
console.log('obj.then');
}
}).then(onFulfilled, onRejected);
완전한 테스트 DEMO:promise 实例
六.고급 사용법
-
제네레이터 (
generator) 와 연동 -
async/await와 연동
(분량 제한으로, 나중에 설명)
아직 댓글이 없습니다