体感!JavaScriptで超速アプリケーション開発 -Meteor完全解説

第3回Meteorの基本的なAPI

Meteorの基本的なAPIを紹介

前回は、Meteorプロジェクトのディレクトリ構造と、それらがMeteorによって実行時にどのように取り扱われるかを解説しました。それにより、MeteorはクライアントとサーバをどちらもJavaScriptで記述でき、コードの共有も非常に容易だということを明らかにしました。

今回は、⁠サーバとクライアントのどちらもJavaScriptで書ける」というMeteorの特色を示すような、Meteorの基本的なAPIについて解説します。

MeteorのコアAPI

まずは最も基本的なAPIとして、以下の3つがあります。

Meteor.is_client
現在のコードがクライアント上で実行されている場合はtrue
Meteor.is_server
現在のコードがサーバ上で実行されている場合はtrue
Meteor.startup()
アプリケーションの開始後に呼び出される。

前回学んだように、プロジェクト内のclientserverpublic以外の場所にあるJavaScriptコードは、サーバ上でもクライアント上でも実行されます。そして、Meteor.is_clientMeteor.is_serverといった変数を使用すると、現在のプログラムがサーバ上で実行されているのか、クライアント上で実行されているのかを判定することができるわけです。

また、Meteor.startup()は、クライアント上かサーバ上かにかかわらずアプリケーションの開始後に呼び出されます。クライアント上ではDOMが構築され、Meteorの初期処理が終わったタイミングで、サーバ上ではプロセスが起動して初期処理が完了したタイミングで呼び出されます。

これらを使用したコード例を以下に掲載します。以下のコードは、Meteor.startup()内でMeteor.is_clientMeteor.is_serverを使用して処理の振り分けを行っています(ログに出力するメッセージを変えているだけです⁠⁠。

リスト1 sample3-1/sample3-1.js
Meteor.startup(function() {
  if (Meteor.is_client) {
    console.log("クライアントの初期化完了!");
  } else {
    console.log("サーバの初期化完了!");
  }
});

サーバ上では、meteorコマンドを実行したコンソール上にメッセージが表示されます。

図1 サンプル3-1の実行結果(サーバ)
図1 サンプル3-1の実行結果(サーバ)

クライアント上では、画面が表示された直後にブラウザのコンソールにメッセージが出力されました。サーバとクライアントで全く同じコードが動作しつつ、異なる処理が行われていることがおわかりかと思います。

図2 サンプル3-1の実行結果(クライアント)
図2 サンプル3-1の実行結果(クライアント)

サンプルは以下からダウンロードできます。

サーバのメソッドをクライアントから呼び出す

Meteor.methods()を使用すると、サーバ上で定義したメソッドをクライアントから呼び出すことも可能です。

Meteor.methods()の引数は、メソッド名をキー、関数オブジェクトを値としたJavaScriptオブジェクトになります。以下のコードを見れば一目瞭然です。

Meteor.methods({
  // helloメソッドの定義
  hello: function(firstName, lastName) {
    return "Hello, " + firstName + " " + lastName;
  }
});

上のコードがサーバ上で実行されると、クライアントからの呼び出しが可能なhello()というメソッドが定義されます。

Meteor.methods()で定義されたメソッドをクライアントから呼び出すには、Meteor.call()もしくはMeteor.apply()を使用します。これらのメソッドの違いは、apply()は呼び出すメソッドの引数を配列で指定するのに対し、call()は可変長引数で指定するというものです。また、引数の最後には関数オブジェクトを指定することができ、リモートメソッドの戻り値を受け取るためのコールバック関数を渡すことができます。コールバックは2つの引数を取ります。1つ目はエラーが発生した時のエラーオブジェクト、2つ目は正常終了時の戻り値です。

たとえば、上のhello()Meteor.call()を使用して呼び出すと以下のようになります。

// callの第一引数はメソッド名
// 第二引数からコールバックの手前までが、サーバ上のメソッドに渡す引数
Meteor.call('hello', 'Shumpei', 'Shiraishi', function(error, result) {
  // callback
});

これをMeteor.apply()を使用して呼び出すと以下のようになります。

// applyの第一引数はメソッド名
// 第二引数の配列が、サーバ上のメソッドに渡す引数
Meteor.apply('hello', ['Shumpei', 'Shiraishi'], function(error, result) {
  // callback
});

簡単なサンプルとともに、Meteor.methods()の動作を確認してみましょう。まずはHTMLのソースコードから。

sample3-2/sample3-2.html
<head>
  <meta charset="UTF-8">
  <title>sample3-2</title>
</head>
<body>
  名前:
  <input id="name" autofocus>
  <button id="button">クリック</button>
</body>

このHTMLがブラウザにレンダリングされた結果は以下のようになります。

図3 サンプル3-2の画面
図3 サンプル3-2の画面

次に、JavaScriptのソースコードを示します。以下のファイルはプロジェクト直下に保存したsample3-2/sample3-2.jsため、クライアントからもサーバからも実行されます。サーバ上で実行される場合はMeteor.methods()を使用してhello()というメソッドを定義し、クライアント上で実行される場合は、⁠クリック」と書かれたボタンにイベントハンドラを設定しています。イベントハンドラの内部でMeteor.call()を使用し、サーバ側のhello()メソッドを呼び出しています。

// サーバ上で実行されるコード
if (Meteor.is_server) {
    // プロセスが開始したら
    Meteor.startup(function () {
        // hello()メソッドの定義を行なっている
        Meteor.methods({
            hello: function(name) {
                return "Hello, " + name;
            }
        });
    });
}
// クライアント上で実行されるコード
if (Meteor.is_client) {
    // 初期化が完了したら
    Meteor.startup(function() {
        var name = document.getElementById('name');
        var button = document.getElementById('button');
        // ボタンをクリックされると、サーバ上のhello()メソッドを呼び出す
        button.addEventListener('click', function() {
            Meteor.call('hello', name.value, function(error, result) {
                alert(result);
            });
        }, false);
    });
}

実行結果は以下のとおりです。ボタンを押すとサーバ上のメソッドが実行され、その結果をコールバックで受け取った後、alert()で表示しています。

図4 サンプル3-2の実行結果
図4 サンプル3-2の実行結果

サンプルは以下からダウンロードできます。

リモートメソッドのスタブ

Meteor.methods()は、実はクライアント側でも呼び出すことができます。クライアント側でMeteor.methods()を使用すると、サーバサイドのメソッド呼び出しをシミュレートする「スタブ」を作成できます。

作成されたスタブのメソッドは、戻り値や発生した例外が全て無視されるため、呼び出し側のコールバックの引数には常にundefinedが渡されます。こういう動作が何の役に立つのか、筆者はまだいまいち理解できていないのですが、Meteorのドキュメントには「スタブはその副作用のために実行される。スタブは、サーバとのラウンドトリップに伴う遅延なしに、サーバのメソッドが行う動作の結果をシミュレートすることを意図している」とあります。

現在メソッドがスタブとして呼び出されているか否かは、メソッド内でthis.is_simulationという変数にアクセスすればわかります。

Meteor.methods({
  hello: function(firstName, lastName) {
    if (this.is_simulation) {
      // クライアントスタブ上での処理
    } else {
      // サーバ上で実行された際の処理
    }
  }
});

リモートメソッドを並列実行する

Meteor.methods()で作成したメソッドは、1クライアントにつき、1クライアントあたり同時に1つのメソッドしか実行されません。つまり、リモートメソッドは並列実行されないということです。

リモートメソッド内でthis.unblock()を呼び出すことで、この挙動を変更することができます。このメソッドを呼び出すと、メソッド呼び出しがブロックされなくなり、メソッドが並列実行されるようになります。これは処理の並列度を高めることになりますが、サーバ側の資源(スレッド)が大量に消費される可能性も伴いますので、注意が必要です。

Meteor.methods({
  hello: function(firstName, lastName) {
      // 並列実行を許可する
      this.unblock();
      ...
  }
});

まとめ

今回は、Meteorの実行環境上で、サーバとクライアントのコードがシームレスに連携するためのAPIを紹介しました。次回からはいよいよ、本格的なアプリケーション開発に必要なMVCアーキテクチャなどに関する説明に入ります。

おすすめ記事

記事・ニュース一覧