前置き
単語の対応する翻訳に注意し、パターン全体の理解に役立てます
-
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(已拒否)
後の 2 つを総称して 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 オブジェクトを返す(チェーン呼び出しが可能)、簡単に以下のように理解:
-
onFulfilledがトリガーされた場合、該オブジェクトの [[PromiseValue]]=onFulfilled の戻り値、[[PromiseStatus]]=resolvednew Promise(function(resolve, reject) { resolve(2); }).then(function(future) { return future * 2; }, onRejected).then(onFulfilled, onRejected); // onFulfilled が 4 を取得
-
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 中に任意の 1 つの 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と連携
(篇幅の制限により、後で説明)
コメントはまだありません