Posted at

BaaS@rakuzaのCordovaプラグインをPromiseに対応させる

More than 1 year has passed since last update.

BaaS@rakuzaのCordovaプラグイン(RKZClient)の各関数は非同期処理のため、引数に成功時・失敗時のコールバック関数を受け取るようになっています。

そのため、A処理をしてからB処理をする場合に、処理がネストしてソースコードが見づらくなってしまいます。(いわゆる、コールバック地獄)

エラー処理が何度も出てくるし、微妙・・


MainController.js

RKZClient.setTenantKey(TENANT_KEY, function() {

// 初期化が終わったら、ユーザーを登録
var userData = {};
RKZClient.registUser(userData, function(userData) {
// ユーザー登録が終わったらプッシュ通知の初期化(デバイストークンの登録)
setupPushNotification(userData.user_access_token);
}, function () {
// エラー処理
});
}, function(error) {
// エラー処理
});

引数にコールバック関数を受け取る方法ではなく、Promiseを返す様にしたら解決します。

以下のようなイメージ

エラー処理も1つにまとまり、すっきり


MainController.js

setTenantKey().then(function() {

// 初期化が終わったら、ユーザーを登録
var userData = {};
return registUser(userData);
}).then(function (userData) {
// ユーザー登録が終わったらプッシュ通知の初期化(デバイストークンの登録)
setupPushNotification(userData.user_access_token);
}).catch(function(error) {
// エラー時にアラートでエラー内容を表示します
alert(JSON.stringify(error, null, ' '));
});

function setTenantKey() {
var deferred = $q.defer();

RKZClient.setTenantKey(TENANT_KEY, function() {
deferred.resolve();
}, function(error) {
deferred.reject(error);
});

return deferred.promise;
}

function registUser(userData) {
var deferred = $q.defer();

RKZClient.registUser(userData, function(userData) {
deferred.resolve(userData);
}, function(error) {
deferred.reject(error);
});

return deferred.promise;
}


ただ、RKZClient呼び出すごとにPromiseでラップする処理を書くのは面倒です。

そこで、RKZClient自体をラップしてみます。ついでに、PromiseはES2015のオブジェクトを使います。


RKZClientPromise

(function (global) {

global.document.addEventListener('deviceready', onDeviceReady);

function onDeviceReady() {
var originalClient = global.RKZClient;

// RKZClientの各関数をProxy関数で上書き
// (RKZClientのプロトタイプのプロパティのみ処理していることに注意)
Object.keys(Object.getPrototypeOf(originalClient)).filter(function (key) {
return typeof originalClient[key] === 'function';
}).forEach(function (key) {
console.log(key);
originalClient[key] = createProxy(originalClient[key]);
});

function createProxy(originalFunc) {
return function () {
var args = Array.prototype.slice.call(arguments);
var handlers = args.filter(function (arg) {
return typeof arg === 'function';
});
// ハンドラーが引数にある場合は、元の関数を実行
if (handlers.length != 0) {
return originalFunc.apply(this, args);
}
// ハンドラーが引数にない場合は、Promiseを返す
return new Promise(function (resolve, reject) {
args.push(function () {
resolve.apply(this, arguments);
});
args.push(function () {
reject.apply(this, arguments);
});
originalFunc.apply(this, args);
});
};
}
}
})(window);



index.html

<script>

ons.bootstrap('myApp');
</script>
<script src="js/RKZClientPromise.js"></script> <!-- MainController.jsより先に読み込む -->
<script src="js/controllers/MainController.js"></script>

これで、RKZClientの各関数がPromiseを返す様になります。


MainController.js

RKZClient.setTenantKey(TENANT_KEY).then(function() {

// 初期化が終わったら、ユーザーを登録
var userData = {};
return RKZClient.registUser(userData);
}).then(function (userData) {
// ユーザー登録が終わったらプッシュ通知の初期化(デバイストークンの登録)
setupPushNotification(userData.user_access_token);
}).catch(function(error) {
// エラー時にアラートでエラー内容を表示します
alert(JSON.stringify(error, null, ' '));
});

Promiseに対応していないライブラリがあれば、同じ様にラップしてみても良いかもしれません。

※公式が対応してくれるのが一番ですが