[node.js] 関数の戻り値を作成し、変数に格納したい

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 8,217

lirlia

score 36

前提・実現したいこと

node.jsを使用しAmazonのDynamoDBに接続して、テーブル内クエリを取得しようとしています。

console.logに出力はできたのですが、これを変数に格納する方法がわかりません。

該当のソースコード

function getAllQuery(table_name) {

  var params = {
    TableName: table_name,
    Select: "ALL_ATTRIBUTES"
  };
  var result = dynamodb.scan(params, function (err, data) {
    if (err) {
      console.log(err); // エラー時
    } else {
      console.log(data['Items']);
    }
  });
};

getAllQuery(TABLE_ACCOUNT_LIST);

理想としては下記のように、関数の戻り値にクエリを格納したいと思っております。

var result = getAllQuery(TABLE_ACCOUNT_LIST);

いろいろと調べてはいるのですが、調べ方のとっかかりがなく困っています。
どのように関数を改変すれば良いか教えていただけないでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+4

Javascriptでは、外部とのI/Oがある場合、非同期に処理する必要があります。まれに同期的な関数が用意されている場合もありますが、基本的には、コールバック関数によって、「非同期にデータを受け取らなければならない」と思ってください。
従いまして、残念ながら、

var result = getAllQuery(TABLE_ACCOUNT_LIST);

のように呼び出すことはできません。livetc様の回答のように

getAllQuery(table_name, callback){ 
// 処理 
callback(data['Items']); 
}

というようにサブルーチンを作成し、呼び出し側も

getAllQuery(TABLE_ACCOUNT_LIST, function (result) {
  // result 受け取り後の処理
});

と書くようにする必要があります。これがコールバック地獄と呼ばれており、この function のネストが深くなりすぎないようにする技がいろいろとありますので、「Javascript コールバック地獄」などで検索してみてください。

参考:Node.jsのコールバック地獄をPromiseやGeneratorを使って解消する

コールバックによる非同期動作について

他の回答へのコメントを見ていて、コールバックによる非同期動作について解説する必要があるかと思い、追記します。

もとのコードのdynamodb.scan を呼び出した時、この API はその終了を待たずに帰ってきます。
すると、getAllQueryの実行自身も終了し、 function (err, data) {}で記述した内容は実行を保留された状態になります。この保留された内容はプログラム全体が終了まで実行されません。したがいまして、

var result = getAllQuery(TABLE_ACCOUNT_LIST);
// result を参照する処理

と書きたくても、 result に結果を入れることは不可能なのです。一旦、プログラムを終了すれば、保留されていたコールバック(function (err, data) {}で記述した内容)が実行されます。そして、そこから新たな旅を始めなければなりません。

node.js はシングルスレッドで動作しており、 

  1. 読み込んだプログラムの実行
  2. 保留されたコールバックのうち、実行可能なもの(I/Oが終わったもの)を一つ選び実行
  3. 2.を繰り返し、保留されたコールバックがなくなるとプロセスを終了

という処理を行います。シングルスレッドであるため、I/O ごとにスレッドの実行を保留することは性能上許されません。そのかわりにマルチスレッドプログラミングのように微妙なタイミングを考えてmutex を使ったりする必要が無いわけです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/06 22:43

    ご丁寧なご回答ありがとうございました。

    コールバックの仕様的にどうやるのだろう?と思っていたのですが、「不可能」と断定いただきすっきりしました。

    キャンセル

+2

getAllQuery(table_name, callback){
// 処理
callback(data['Items']);
}
とかですかね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/06 10:20

    いただきましたご指摘をコードに反映しました。

    ```

    function getAllQuery(table_name, callback) {

    var params = {
    TableName: table_name,
    Select: "ALL_ATTRIBUTES"
    };
    dynamodb.scan(params, function (err, data) {
    if (err) {
    console.log(err); // エラー時
    } else {
    callback(data['Items']);  ← callbackに変更
    }
    });
    };

    ```

    console.logでの呼び出し方はわかるのですが、変数に格納する方法は相変わらずわからないため、こちらも方法を教えていただけないでしょうか。

    ```
    getAllQuery(TABLE_ACCOUNT_LIST, function(data){
    console.log(data);
    }));
    ```

    キャンセル

+1

returnしても取得できないのですか?

http://www.ajaxtower.jp/js/function/index4.html

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/06 09:50 編集

    ご回答ありがとうございます。

    実は、returnもためしてみたのですが値が帰ってきません。
    コールバック関数内に仕込んだreturn結果の受け取り方がわからず困っています。

    function getAllQuery(table_name) {

    var params = {
    TableName: table_name,
    Select: "ALL_ATTRIBUTES"
    };
    var result = dynamodb.scan(params, function (err, data) {
    if (err) {
    console.log(err); // エラー時
    } else {
    //console.log(data['Items']);
    return data['items'];
    }
    });
    };

    キャンセル

  • 2016/11/06 10:32

    すいません、コードよく見てませんでした

    キャンセル

  • 2016/11/06 11:23

    コード軽く読み飛ばして普通の関数(同期)かのように勘違いしてました、非同期に関する回答別の方が素晴らしい回答されてますのでそちらを参考にしてください。

    非同期は、
    「この仕事(※1)やっといてよ、俺は自分の仕事あるからそっちやっとくから(※2)、終わったら結果を喫煙所(※3)で教えてね」
    という感じの処理で
    今回のばあい
    (※1)DBのscan
    (※2)dynamodb.scanの下の処理(関数終了する)
    (※3)callbackという場所してい

    なのでgetAllQueryが終了した時にはまだscanの結果は存在しないという事です。

    非同期の考え方は
    http://qiita.com/YoshikiNakamura/items/732ded26c85a7f771a27
    の「非同期処理を行なう関数を作る(コールバック版)」あたり参考にされてくださいm(_ _)m

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる