Learning ECMAScript6 が参考書籍
Promise はモナド
計算中かもしれない値を管理する文脈を持つモナド。
非同期操作を依存関係に従ってパイプライン化することでネストや処理の分離をなくし、コールバックやイベントを用いたコードよりも可視化性を高める。
プロミスはモナドである認識を持つことにより、多少 Haskell の知識がある自分としては、プロミスの必要性や位置付けを理解しやすくなった。
以降は、プロミスの仕様とメソッドの例などを整理している。
The Promise Constructor
Promise コンストラクタは、Promise オブジェクトを作るために使われる。 Promise オブジェクトは非同期操作を表現するオブジェクト。
Promise コンストラクタには executor と呼ばれるコールバック関数を渡し、このexecutor にはresolve
とreject
の二つのコールバック関数を渡す。
resolve
/reject
の実態は、その後then()
で渡される処理が合成されたもの?なので、プログラマが明示的にこれらの関数を直接実装することはない。
プロミスのステータス
プロミスオブジェクトのステータスは、次のように分類される。
status | description |
---|---|
Fulfilled |
resolve 関数が、non-promise なオブジェクトとともに呼び出された時 |
Rejected |
reject 関数が呼び出されたり、executor のスコープで例外が発生した時 |
Pending |
resolve やreject がまだ呼び出されていない状態 |
Settled | Pending ではない時(ref) |
Once a Promise is fulfilled or rejected, it cannot be transitioned back. An attempt to transition it will have no effect.
一度 Fulfilled または Rejected になると、その状態を戻すことはできない。やろうとしても、何も起きない。
プロミスの特徴として、resolve
関数に Promise オブジェクトが渡った場合、元のオブジェクトのステータスは渡されたオブジェクトのステータスに依存することになる。
// プロミスにプロミスを渡す
let a = new Promise(function (resolve, reject) {
let b = new Promise(function (res, rej) {
setTimeout(function () {
res(100);
}, 1000);
});
resolve(b);
});
a
.then(function (value) {
console.log('the value fulfilled is ' + value);
return value * 3; // return the value multiplied 3
})
.then(function (value) {
console.log('the value passed by previous then is :' + value);
});
The then(onFulfilled, onRejected) method
The callbacks passed to the then() method are executed asynchronously.
then
に渡されるコールバック関数は非同期処理となる。
The then() method always returns a new promise object, which resolves the return value of the calling callback.
then
は必ず promise object を返し、このオブジェクトは渡されたコールバック関数がreturn
した値をresolve
する。
The catch(onRejected) method
then(null, onRejected)
のシンタックスシュガー。
promise.then(null, function(reason){
});
promise.catch(function(reason){
});
catch
も promise object を返し、、このオブジェクトは渡されたコールバック関数がreturnした値をresolve
する。つまり、さらにthen
よるチェーンを作ることができる。
let a = new Promise(function (resolve, reject) {
let b = new Promise(function (res, rej) {
setTimeout(function () {
rej(100);
}, 1000);
});
resolve(b);
});
a
.then(function (value) {
console.log('the value fulfilled is ' + value);
return value * 3; // return the value multiplied 3
})
.catch(reason => {
console.log('error : ' + reason);
return reason;
})
.then(value => { // チェーン可能。onRejectの返り値を引数にとっている
console.log('re:value: ' + value);
});
モナドっぽい雰囲気
普通の値を、プロミスという文脈に包む関数が用意されている。
The Promise.resolve(value) method
The resolve() method is basically used to convert a value to an promise object. It is useful when you find yourself with a value that may or may not be a Promise, but you want to use it as a Promise.For example, the jQuery Promises have different interfaces than the ES6 Promises. Therefore, you can use the resolve() method to convert the jQuery Promises into the ES6 Promises.
これから使おうとしている値が promise object かどうかわからない時、これを使うとプロミスで包むことができる。
The Promise.reject(value) method
主にデバッグ目的で利用する。値をプロミスで包むため、という使われ方はしない。
The Promise.all(itearable) method
iterable オブジェクトの中の promise object が全て fulfill されると、このpromise object も fulfill される。
The Promise.race(iterabe) method
The the race() method of the Promise object takes an iterable object as the argument and returns a Promise that fulfills or rejects as soon as one of the Promises in the iterable object is fulfilled or rejected, with the fulfillment value or reason from that Promise.
iterable オブジェクトの中の promise object のうち、最も早く settled となったオブジェクトが返る。以降、その他のオブジェクトも race
の制御とは無関係にresolve
されていく。
let p1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve("Fulfillment Value 1");
}, 1000);
});
let p2 = new Promise(function(resolve, reject){
setTimeout(function(){
reject("fulfillment Value 2");
}, 2000);
});
var arr = [p1, p2];
Promise.race(arr).then(function(value){
console.log('onFulfilled: ' + value); //Output "onFulfilled: Fulfillment Value 1"
}, function(reason){
console.log('onRejected: ' + reason);
});