まめージェント

Android, GAE, AngularJSの技術ネタ中心。Twitter: @mame01112

AngularJSのPromiseを使ってみた

promise、使ってみた。

今回は、これまで使い方が曖昧だったpromiseに関して多少踏み込んで勉強したのでそちらをシェア。日本語のpromiseの情報も少ないので、多少は参考になるのではないかと思います。ただ、promiseのすべてを網羅しているわけではなく、あくまで”どんな感じで使えばいいか”がわかる内容になっています(このテの話は、最初の導入コストが高いと思っているので、そこを乗り越える助けになれば・・・。)

使い方

まずは、

var deferred = 	$q.defer();

で、defferedのインスタンスを取得します。

そして、promise.thenメソッド側で値を受け取るために、3つのコールバックをを呼びます。

deferred.notify() (処理中であることを通知する)
deferred.resolve() (処理が成功したことを通知する)
deferred.reject() (処理が失敗したことを通知する)

です。
つまり、処理をする側で

deferred.notify("処理中");
deferred.resolve("処理に成功");
deferred.reject("処理に失敗");

と読んであげれば、それがthenで受け取る側に通知されるわけです。
※もちろん、上記のnotify, resolve, rejectの引数は、文字列でなくてもOKです。

ちょっとわかりにくいかもしれないので、例で。
たとえば、下記のようなコードがある場合、

deferred.notify("処理中");

if(isSuccess){
    deferred.resolve("処理に成功");
} else {
    deferred.reject("処理に失敗");
}

isSuccessのbooleeanがtrueであれば、受け取る側はnotify( = 処理中)、resoleve ( = 処理に成功)
isSuccessのbooleeanがfalseであれば、受け取る側はnotify( = 処理中)、reject ( = 処理に失敗)
というように順番に値を受け取れるようです。
※このあたりが、いろいろなサイトを見ていてあまり書かれていなかったポイントなので(もしかしたら自明すぎるから書いていないのかもしれませんが)、ちょっと詳しく書いてみました。

そして、最後はpromiseオブジェクトを返してあげる必要があります。

この内容を踏まえて、処理全体の流れはこんな感じ。

function loadSomething()
{
	deferred.notify("処理中");

	//ここで何かをロードしたり・・・。

	if(isSuccess){
	    deferred.resolve("処理に成功");
	} else {
	    deferred.reject("処理に失敗");
	}
return deferred.promise;
}

もちろん、不要なコールバックは呼び出さなくてOKです。

これで処理をする側はOK。

この処理の結果を受け取る側(このメソッドを呼び出す人)は、promise.then()を呼び出します。こんな感じ。

loadSomethingService.load().then(
	function(successResponse){
		// TODO
	}, function(errorResponse){
		// TODO
	}, function(notifyResponse){
		// TODO
	}
);

上記の例だと、successResponseは"処理に成功"を、errorResponseは"処理に失敗"を、notifyResponseは"処理中"をそれぞれ受け取れます。

複数のpromiseの処理が終わったときにコールバックをもらう

複数の処理をやった後、最後にまとめてコールバックがほしい・・・というケースも多いかと思います。僕の場合だと、複数のリソースを読み込んだ後ServiceからControllerに値を返したい、的な。

そのときは、$q.all()を使うことができます。たとえばこんな感じ。

var categories = [];
var promises = [];

for(var i=0; i<10; i++) {
	var promise = getPromise(i);
	promises.push(promise);
}

return $q.all(promises);

受け取る側は、ふつうのpromiseと同じ受け取り方が可能です(上記参照)。

promiseを使えば、非同期処理が必要な部分をServiceとかFactoryに切りだすことができそうです。他にもPromise オブジェクトのメソッドチェーンなどが使えるようですが、それはまたの機会に。