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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Node.js

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

JavaScript

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

Q&A

解決済

1回答

1392閲覧

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

nobodytolove123

総合スコア61

Node.js

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

JavaScript

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

0グッド

1クリップ

投稿2017/12/07 06:01

###前提・実現したいこと
お世話になります。

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

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

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

###該当のソースコード

js

1const imgObject = []; 2//contentに画像取得IDが格納されている 3for (let i in content) { 4 //事前にIDが空の場合には__LINE_TOKEN__文字列が入れてある。 5 //IDが格納されている場合にはバイナリデータを取得。 6 if (String(content[i].object).indexOf("__LINE_TOKEN__") !== 0) { 7 //APIヘッダー 8 var send_options = { 9 host: 'api.line.me', 10 path: '/v2/bot/message/' + content[i].object + '/content', 11 headers: { 12 "Content-type": "application/json; charset=UTF-8", 13 "Authorization": " Bearer {" + Token + "}" 14 }, 15 method: 'GET' 16 }; 17 18    //取得したバイナリデータをdata配列に格納 19 var req = https.request(send_options, function (res) { 20 var data = []; 21 //データを連続的に格納。 22 res.on('data', function (chunk) { 23 data.push(new Buffer(chunk)); 24 }).on('error', function (err) { 25 console.log(err); 26 }).on('end', function () { 27 //データが細切れになっているので連結。 28 var result = Buffer.concat(data); 29 imgObject.unshift(result); 30 } 31 }); 32 }); 33 req.end(); 34 } else{ 35 //IDが空の場合には再び__LINE_TOKEN__文字列を入れる。 36 imgObject.unshift('__LINE_TOKEN__'); 37 } 38 }

###出力

js

1[ 2'__LINE_TOKEN__,', 3'', 4'__LINE_TOKEN__,', 5'__LINE_TOKEN__,', 6'__LINE_TOKEN__,', 7'', 8'', 9'__LINE_TOKEN__,', 10'__LINE_TOKEN__,', 11'', 12'__LINE_TOKEN__,', 13'__LINE_TOKEN__,', 14'__LINE_TOKEN__,', 15'__LINE_TOKEN__,', 16'__LINE_TOKEN__,', 17'__LINE_TOKEN__,', 18'__LINE_TOKEN__,', 19'__LINE_TOKEN__,', 20'__LINE_TOKEN__,', 21'__LINE_TOKEN__,', 22'__LINE_TOKEN__,', 23'__LINE_TOKEN__,', 24'__LINE_TOKEN__,', 25'__LINE_TOKEN__,', 26'__LINE_TOKEN__,', 27'__LINE_TOKEN__,' 28]

問題点

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

js

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

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

js

1'__LINE_TOKEN__,' 2'__LINE_TOKEN__,' 3'__LINE_TOKEN__,' 4'__LINE_TOKEN__,' 5'__LINE_TOKEN__,' 6'__LINE_TOKEN__,' 7'__LINE_TOKEN__,' 8'__LINE_TOKEN__,' 9'__LINE_TOKEN__,' 10'__LINE_TOKEN__,' 11'__LINE_TOKEN__,' 12'__LINE_TOKEN__,' 13'__LINE_TOKEN__,' 14'__LINE_TOKEN__,' 15'__LINE_TOKEN__,' 16'__LINE_TOKEN__,' 17'__LINE_TOKEN__,' 18 19<buffer ff 8d ...> 20<buffer ff 8d ...> 21<buffer ff 8d ...> 22<buffer ff 8d ...> 23<buffer ff 8d ...> 24<buffer ff 8d ...> 25<buffer ff 8d ...> 26<buffer ff 8d ...> 27

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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

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

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

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

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

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

javaScript

1const async = require('async'); 2 3// functionsはコールバック関数を求める関数に生まれ変わりました。 4const functions = content.map(it => done => { 5 // contentの1要素はit引数に束縛されています。これで多様性を演出 6 // ガード節を使うとネストが減るのが好きで勝手にリファクタリングしてます 7 if (String(it.object).indexOf("__LINE_TOKEN__") === 0) { 8 imgObject.unshift('__LINE_TOKEN__'); 9 done(null, null); 10 return; 11 } 12 const send_options = { 13 host: 'api.line.me', 14 path: `/v2/bot/message/${it.object}/content`, 15 headers: { 16 "Content-type": "application/json; charset=UTF-8", 17 "Authorization": " Bearer {" + Token + "}", 18 }, 19 method: 'GET', 20 }; 21 const req = https.request(send_options, res => { 22 var data = []; 23 res 24 .on('data', chunk => { data.push(new Buffer(chunk)) }) 25 .on('error', err => { done(err, null) }) 26 .on('end', () => { 27 //データが細切れになっているので連結。 28 var result = Buffer.concat(data); 29 imgObject.unshift(result); 30 done(null, result); 31 }); 32 }); 33}); 34 35async.series(functions, (err, results) => { 36 if (err) console.error(err); 37 // results内にdoneの第二引数の配列が詰まっているはずなので確認してください。 38 console.log(results); 39 console.log(results.filter(it => it != null)); // Null除去 40});

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

投稿2017/12/07 07:18

編集2017/12/07 07:22
miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobodytolove123

2017/12/07 07:35

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問