質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

91.37%

  • JavaScript

    11187questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Node.js

    1219questions

    Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

javaScriptでデータをすべて受け取ってから処理を進めたい

解決済

回答 1

投稿 2017/12/07 15:01

  • 評価
  • クリップ 1
  • VIEW 68

前提・実現したいこと

お世話になります。

Line Messaging APIでWebページにLineアプリ内の画像やテキストデータと同じものを写すようにするのに試行錯誤しています。

Lineアプリのようにメッセージや画像を送信した順に表示される様にfor文の中で
IDを元に非同期で画像データを取得しています。

現在、データベースにLineで送信したテキストや画像データを取得できるIDが入っています。

該当のソースコード

const imgObject = [];
//contentに画像取得IDが格納されている
for (let i in content) {
      //事前にIDが空の場合には__LINE_TOKEN__文字列が入れてある。
      //IDが格納されている場合にはバイナリデータを取得。
      if (String(content[i].object).indexOf("__LINE_TOKEN__") !== 0) {
        //APIヘッダー
        var send_options = {
          host: 'api.line.me',
          path: '/v2/bot/message/' + content[i].object + '/content',
          headers: {
            "Content-type": "application/json; charset=UTF-8",
            "Authorization": " Bearer {" + Token + "}"
          },
          method: 'GET'
        };

    //取得したバイナリデータをdata配列に格納
        var req = https.request(send_options, function (res) {
          var data = [];
          //データを連続的に格納。
          res.on('data', function (chunk) {
            data.push(new Buffer(chunk));
          }).on('error', function (err) {
            console.log(err);
          }).on('end', function () {
            //データが細切れになっているので連結。
            var result = Buffer.concat(data);
            imgObject.unshift(result);
            }
          });
        });
        req.end();
      } else{
            //IDが空の場合には再び__LINE_TOKEN__文字列を入れる。
            imgObject.unshift('__LINE_TOKEN__');
     }
    }

出力

[
'__LINE_TOKEN__,',
'',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'',
'',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,',
'__LINE_TOKEN__,'
]

 問題点

バイナリデータをイベントハンドラを用いて取得しているのでfor文内で処理が後回しにされてしまいます。

on('data'){ ...
on('end'){ ...


配列に格納しないでコンソールにif条件文の結果を出力すると下記のようになります。

'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'
'__LINE_TOKEN__,'

<buffer ff 8d ...>
<buffer ff 8d ...>
<buffer ff 8d ...>
<buffer ff 8d ...>
<buffer ff 8d ...>
<buffer ff 8d ...>
<buffer ff 8d ...>
<buffer ff 8d ...>


理想は取得した順でデータを配列に格納したいです、asyncモジュールなどを使ってみましたがイベントハンドラを同期的に処理することはできませんでした。
どうかお知恵をお貸しください、よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+3

これはNode.js使いが一度は絶望する箇所ですね。
可変数の要素を非同期で回すにはテクニックが必要です。

asyncモジュールなどを使ってみましたがイベントハンドラを同期的に処理することはできませんでした。

asyncモジュールなら可能ですよ。
予めコールバック関数を引数として宣言しておいて、
最後のres.on('end', fn);の実行が終わったらコールバック関数を叩けば良いでしょう。

そしてasyncはseriesがめっちゃ書きやすいです。
JavaScriptのasync.jsでwaterfallとseries、parallelの違い - Qiita

ささっと書くとこんな感じ
(動作検証はせずに平でばばーと書いたのでエラーとかあるかも(´・ω・`))

  • contentは普通のArrayだと思ってArray.prototype.mapとか使って加工してます
  • アロー演算子とか使いまくってES2015丸出しなのでちょっと読みづらいかもしれません
const async = require('async');

// functionsはコールバック関数を求める関数に生まれ変わりました。
const functions = content.map(it => done => {
  // contentの1要素はit引数に束縛されています。これで多様性を演出
  // ガード節を使うとネストが減るのが好きで勝手にリファクタリングしてます
  if (String(it.object).indexOf("__LINE_TOKEN__") === 0) {
    imgObject.unshift('__LINE_TOKEN__');
    done(null, null);
    return;
  }
  const send_options = {
    host: 'api.line.me',
    path: `/v2/bot/message/${it.object}/content`,
    headers: {
      "Content-type": "application/json; charset=UTF-8",
      "Authorization": " Bearer {" + Token + "}",
    },
    method: 'GET',
  };
  const req = https.request(send_options, res => {
    var data = [];
    res
      .on('data', chunk => { data.push(new Buffer(chunk)) })
      .on('error', err => { done(err, null) })
      .on('end', () => {
        //データが細切れになっているので連結。
        var result = Buffer.concat(data);
        imgObject.unshift(result);
        done(null, result);
      });
  });
});

async.series(functions, (err, results) => {
  if (err) console.error(err);
  // results内にdoneの第二引数の配列が詰まっているはずなので確認してください。
  console.log(results);
  console.log(results.filter(it => it != null)); // Null除去
});

まぁ、Promiseとasync/awaitを覚えた方が超絶綺麗に書けるのでそちらの方が良いと思います。

投稿 2017/12/07 16:18

編集 2017/12/07 16:22

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/12/07 16:35

    ご回答ありがとうございます!ES2015にわかの僕では内容の理解が難しい 笑 早速コードに盛り込んでみようと思います!確かに非同期対策の習得をすぐにでもしないとですよね...

    キャンセル

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

ただいまの回答率

91.37%

関連した質問

同じタグがついた質問を見る

  • JavaScript

    11187questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Node.js

    1219questions

    Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。