Promise の利用

Kii Cloud SDK for JavaScript では、API の非同期的な実行を実現するため、コールバック関数に加え、Promise を使った実装をサポートしています。

Java Script SDK では、時間がかかる API をノンブロッキング API で実現しています。新規ユーザーの登録やデータのアップロード等、SDK が提供する多くのメソッドはサーバーとの通信が発生する関係で処理完了までに時間を要します。これらの処理は非同期に行い、処理完了後の動作を Promise またはコールバック関数で記述できます。

処理完了後の動作は、次のいずれかの形式で記述します。

  • Promise

    Promise を使ってノンブロッキング API の処理を記述できます。

    ネットワークアクセスを行う各 API は Promises/A+ に準拠した Promise オブジェクトを返します。アプリでは Promise に基づいた処理を記述することで、下記のコールバック関数のようにネストが深くなる問題を回避できます。

    KiiUser.authenticate("user_123456", "123ABC").then(
      function(theAuthenticatedUser) {
        // Do something with the authenticated user.
      },
      function(error) {
        // Do something with the error response.
      }
    );
    
  • コールバック関数

    処理が完了した際に実行される関数を API に渡すと、API の実行完了時に SDK 側から呼び出します。

    シンプルな仕組みですが、複数の API を順番に呼び出す処理を記述すると、ネストが非常に深くなる問題(いわゆる「コールバック地獄」)が発生します。

    KiiUser.authenticate("user_123456", "123ABC", {
      success: function(theAuthedUser) {
        // Do something with the authenticated user.
      },
      failure: function(theUser, anErrorString) {
        // Do something with the error response.
      }
    });
    

アプリを新規に実装する場合は、複雑な処理でもシンプルに記述できる Promise を使った実装方法をおすすめします。

JavaScript で利用できる Promise の記述方法は様々な形式がありますが、Kii JavaScript SDK では今後主流になると思われる Promises/A+ を採用しています。使用する Promise の実装により、メソッド名などの書式が異なるものがあるため、一般のサイトから情報を探す場合にはご注意ください。

API の書式上、各 API は Promise とコールバックの両方を記述できるようになっていますが、どちらか一方を選択して実装します。たとえば、Promise を使う場合、API の呼び出し時にコールバック関数を渡さないようにしてください。なお、アプリ内の実装箇所によって、Promise を使う部分と、コールバックを使う部分が混在する使い方は問題ありません。

実装例

以下にコールバック関数と Promise を使ったアプリの実装例を示します。

以下の例で使用している各 API の使用方法は、本ガイドの詳細ページで順に解説します。ここでは Promise を使う場合の利点のみをご確認ください。

コールバック関数による実装例

コールバック関数を使ったアプリの実装例を以下に示します。

このアプリでは 3 回の API 呼び出しを行っています。

  1. authenticate によってユーザーをログイン
  2. 成功したときは app_bucket 上に score = 987 というデータを持った Object を save によって作成
  3. さらに成功したときは listACLEntries によって、今、作成した Object の ACL を取得してアクセス権を確認

連続する処理を実現するには、成功のコールバック関数内に次の処理を記述する必要があるため、API を呼び出すたびにネストが深くなり、プログラムの見通しが悪くなっていきます。同時に、API の失敗時に実行されるコールバック関数との対応も取りづらくなります。

// 1. Log in the user.
KiiUser.authenticate("user_123456", "123ABC", {
  success: function(theAuthedUser) {
    // 2. Create a KiiObject.
    var appBucket = Kii.bucketWithName("app_bucket");
    var obj = appBucket.createObject();
    obj.set("score", 987);
    obj.save({
      success: function(theObject) {
        // 3. Get the ACL.
        var acl = theObject.objectACL();
        acl.listACLEntries({
          success: function(theACL, theEntries) {
            console.log("ACL got!");
            console.log(JSON.stringify(theEntries));
          },
          failure: function(theACL, errorString) {
            console.log("Error listing ACL: " + errorString);
          }
        });
      },
      failure: function(theObject, errorString) {
        console.log("Error saving object: " + errorString);
      }
    });
  },
  failure: function(theUser, anErrorString) {
    console.log("Error authentication: " + errorString);
  }
});

Promise による実装例

Promise を使って、上記のサンプルと同じ機能を実装した例を以下に示します。

コールバック関数を使う例と同様に、authenticatesavelistACLEntries を順に呼び出しています。

ここでは、各 API が返した Promise を使って、処理の連鎖を構築しています。ネストが深くなる現象を回避できると同時に、上から下への処理の流れが明確になります。また、エラー処理をまとめることもできるため(個別に記述することもできます)、正常系の見通しがよくなります。

// 1. Log in the user.
KiiUser.authenticate("user_123456", "123ABC").then(
  function(params) {
    // 2. Create a KiiObject.
    var appBucket = Kii.bucketWithName("app_bucket");
    var obj = appBucket.createObject();
    obj.set("score", 123);
    return obj.save();
  }
).then(
  function(theSavedObject) {
    // 3. Get the ACL.
    var acl = theSavedObject.objectACL();
    return acl.listACLEntries();
  }
).then(
  function(params) {
    console.log("ACL got!");

    var theEntries = params[1];
    console.log(JSON.stringify(theEntries));
  }
).catch(
  function(error) {
    console.log("Error: " + error);
  }
);

なお、catch ハンドラーを記述しておかないと、エラー発生時の処理を取りこぼすことになります。特に、Server Code で動作させる場合は、エラー発生時に処理の完了を表す done() を呼び出すタイミングを失い、処理がタイムアウトすることになるため、ご注意ください。Server Code での done() の詳細は こちら をご覧ください。