環境
Babel で ES6 のコードを ES5 に変換(Promise Babel の polyfill を利用)し、Browserifyで依存するモジュール含めてビルドしています。
このオレオレ ES 6環境は GitHub に置いてあります。
→ https://github.com/hkusu/es6-learning-environment
主要なライブラリのバージョンは次のとおり。
・ babelify:6.1.2
・ browserify:10.2.4
ダミーの WEB-API を用意
非同期通信といえば WEB-API だと思いますが、今回は JavaScript の setTimeout()
を利用して、WEB-API を模した関数を用意しました。
コードは ES6 で書いてます。
// Babel の現バージョンでは Polyfill が必要なので読み込み
require("babelify/polyfill");
// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
return new Promise((resolve, reject) => {
// 2 秒後にメッセージを返す
setTimeout(() => {
resolve("success A");
//reject(new Error("error A")); // エラー発生を模す場合
}, 2000);
});
};
// ダミーの HTTP リクエスト B
var dummyHttpRequestB = () => {
return new Promise((resolve, reject) => {
// 3 秒後にメッセージを返す
setTimeout(() => {
resolve("success B");
//reject(new Error("error B")); // エラー発生を模す場合
}, 3000);
});
};
上記では new Promise()
で Promise オブジェクトを作成していますが、新しめの JQuery だと恐らく $.ajax()
は Promise を返すはずなので、そのまま Ajax で WEB-API を呼び出せばよいと思います。
並列で WEB-API(ダミー) を呼び出す
WEB-API の実行順序を問わない場合です。Promise のスタティックメソッドである Promise.all()
を利用します。
// 並列で A、B を API コール(それぞれの結果は配列にマージされる)
function doParallelRequest() {
return Promise.all([dummyHttpRequestA(), dummyHttpRequestB()]);
}
doParallelRequest().then((message) => {
// リクエスト A、B が完了した場合
console.log(message); // => 3秒後に [ 'return message A', 'return message B' ] が出力される
}).catch(e => {
// リクエスト A、B のどちらかで reject が返された場合
console.log(e);
});
直列で WEB-API(ダミー) を呼び出す
WEB-API A の結果を待ってから、WEB-API B を呼び出すパターンです。例えば後続の WEB-API を呼び出す際のパラメータを組み立てるのに、前の WEB-API のレスポンス結果が必要な場合など。
// 直列で A → B の順番で API コール
function doSerialRequest() {
return dummyHttpRequestA().then((message) => {
// リクエスト A が完了した場合
console.log(message); // => 2秒後に return message A が出力される
return dummyHttpRequestB();
});
}
doSerialRequest().then((message) => {
// リクエスト B が完了した場合
console.log(message); // => 更に3秒後に return message B が出力される
}).catch(e => {
// リクエスト A、B のどちらかで reject が返された場合
console.log(e);
});
補足
then
の中で Promise を return した場合、その then
の実行は中の Promise が resolve されるまで待機します。
この仕組みを使わない場合は、例えば直列の場合だと次のようなコードになります。
dummyHttpRequestA().then((message) => {
// リクエスト A が完了した場合
console.log(message); // => 2秒後に return message A が出力される
dummyHttpRequestB().then(() => {
// リクエスト B が完了した場合
console.log(message); // => 更に3秒後に return message B が出力される
}).catch(e => {
// リクエスト B で reject が返された場合
console.log(e);
})
}).catch(e => {
// リクエスト A で reject が返された場合
console.log(e);
});
【2015.06.29追記】RxJS を利用した場合
RxJS の導入
$ npm install --save-dev rx
動作確認
require("babelify/polyfill");
import Rx from 'rx'
// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
return new Promise((resolve, reject) => {
// 2 秒後にメッセージを返す
setTimeout(() => {
resolve("success A");
//reject(new Error("error A")); // エラー発生を模す場合
}, 2000);
});
};
Rx.Observable.fromPromise(dummyHttpRequestA())
.subscribe((message) => {
console.log(message); // => 2秒後に return message A が出力される
});
直列で WEB-API(ダミー) を呼び出す
require("babelify/polyfill");
import Rx from 'rx'
// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
return new Promise((resolve, reject) => {
// 2 秒後にメッセージを返す
setTimeout(() => {
resolve("success A");
//reject(new Error("error A")); // エラー発生を模す場合
}, 2000);
});
};
// ダミーの HTTP リクエスト B
var dummyHttpRequestB = () => {
return new Promise((resolve, reject) => {
// 3 秒後にメッセージを返す
setTimeout(() => {
resolve("success B");
//reject(new Error("error B")); // エラー発生を模す場合
}, 3000);
});
};
Rx.Observable.fromPromise(dummyHttpRequestA())
.flatMap((message) => {
console.log(message); // => 2秒後に return message A が出力される
return Rx.Observable.fromPromise(dummyHttpRequestB());
})
.subscribe((message) => {
console.log(message); // => 3秒後に return message B が出力される
});
Rx.Observable.fromPromise()
と flatMap()
を利用すると、Promise な非同期通信をストリームに載せて処理することができます。別の Observable なストリームを起点にする場合は次の通り。
require("babelify/polyfill");
import Rx from 'rx'
// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
return new Promise((resolve, reject) => {
// 2 秒後にメッセージを返す
setTimeout(() => {
resolve("success A");
//reject(new Error("error A")); // エラー発生を模す場合
}, 2000);
});
};
// ダミーの HTTP リクエスト B
var dummyHttpRequestB = () => {
return new Promise((resolve, reject) => {
// 3 秒後にメッセージを返す
setTimeout(() => {
resolve("success B");
//reject(new Error("error B")); // エラー発生を模す場合
}, 3000);
});
};
Rx.Observable.return("foo")
.flatMap(() => {
return Rx.Observable.fromPromise(dummyHttpRequestA());
})
.flatMap((message) => {
console.log(message); // => 2秒後に return message A が出力される
return Rx.Observable.fromPromise(dummyHttpRequestB());
})
.subscribe((message) => {
console.log(message); // => 3秒後に return message A が出力される
});