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

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

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

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

JavaScript

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

Q&A

解決済

2回答

7535閲覧

node.js(javascript)の同期処理の方法について

aws

総合スコア48

Node.js

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

JavaScript

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

0グッド

0クリップ

投稿2017/11/12 14:48

node.jsで同期処理をしたいと考えています。
node.js(javascript)初心者ですので変な質問及び理解が足りていないかもしれませんのでご了承下さいませ。

async.seriesを利用すると同期処理(上から順番にプログラムを回したい)が出来るのかと思ったのですがAPIの処理が上手くいきません。
この様にすると良いなど御座いましたらご指摘頂けると幸いです。

現在のコード

var async = require('async'); async.series([ function A_1(callback) { console.log("A_1"); request(options_A1, callback_A1); #<=APIを利用してwebからデータ取得しています。 callback(null); }, function A_2(callback) { console.log("A_2"); request(options_A2, callback_A2); #<=APIを利用してwebからデータ取得しています。 callback(null); }, function A_3(callback) { console.log("A_3"); request(options_A3, callback_A3); #<=APIを利用してwebからデータ取得しています。 callback(null); }, function A_4(callback) { console.log("A_4"); request(options_A4, callback_A4); #<=APIを利用してwebからデータ取得しています。 callback(null); } ], function(err){ console.log('ALL Complete.'); });

各request(option, callback) の所にAPIを利用したデータ取得の処理が入ります。

上記コードの実行結果は

A_1 A_2 A_3 A_4 ALL Complete. #↑ここまでは順番に処理されます。 #以下はAPIの順番ではなく反応順?に返ってきます。 A_2のAPI A_4のAPI A_1のAPI A_3のAPI

求めている実行結果は

A_1 A_1のAPI A_2 A_2のAPI A_3 A_3のAPI A_4 A_4のAPI ALL Complete.

となります。

ALL CompleteしたらDBなどに取得したAPIのデータを保存をしたいと考えています。
現状ですとAPIのデータ取得が完了前にALL Completeのまで進んでしまいデータ保存がうまくいかないです。(正確には保存するデータが何もない。)

何らかの方法で同期処理が出来ればなーと考えています。

宜しくお願いします。

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

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

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

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

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

guest

回答2

0

Javascriptではシングルスレッド前提のため時間がかかる機能(処理開始から処理完了までの経過時間が長い)を実現する関数は多くの場合次のようなコールバック関数を引数にとる非同期関数として提供されています。

function startAsyncSomething(option, callback)
あるいは
function startAsyncSomething(callback, option1, option2, ...)

上記の関数は

「オプションに従って特定の非同期処理を起動する」
「この関数自体は即座にリターンする」
「この関数によって起動した非同期処理が完了したタイミングでcallbackが自動起動される」

という考え方になっています。

質問者さんのコードではrequestがこのような関数であるはずで、各々のrequestの完了時に起動されるコールバックをcallback_Anのように指定しておられます。callback_Anは確かにrequest完了時に起動されているはずですが、問題は「各々の非同期処理が完了したことをseriesに通知する関数である」callbackを非同期関数requestの呼び出し直後に起動している点です。requestは前述のように「即座にリターンする関数」ですので、そのすぐ後にcallbackを起動することは間違いで、「requestで起動した非同期処理が完了した際に呼び出す」ようにせねばなりません。

もしrequestの第二引数のコールバック関数のインターフェースが

function callback_x(err, result)

すなわち第一引数にエラーを、第二引数に正常終了した際の結果を渡すようなものであるならseriesは次のよう用いるべきです。

javascript

1async.series([ 2 function (callback) { 3 console.log("A_1"); 4 request(option_A1, function (err, result) { 5 console.log("A_1 done"); 6 callback(err, result); 7 }); 8 }, 9 ... 10 ], 11 function (err, result) { 12 if (err) { 13 console.log('error: ' + err); 14 } else { 15 console.log('all completed: ' + JSON.stringify(result)); 16 } 17 });

全ての非同期処理がエラーなしに実行されれば結果は次のようになります。
A_1
A_1 done
A_1
A_2 done
...
all completed: [A_1の結果,A_2の結果,...]

投稿2017/11/12 17:09

編集2017/11/12 18:04
KSwordOfHaste

総合スコア18394

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

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

aws

2017/11/12 17:37

ご回答有難う御座います。他のプログラム言語であれば。。。って事を頭の中で何度も考えてました。w 詳細なコード有難う御座います。説明も分かりやすく助かりました。
guest

0

ベストアンサー

JavaScript

1 function A_1(callback) { 2 console.log("A_1"); 3 request(options_A1, callback_A1); #<=APIを利用してwebからデータ取得しています。 4 callback(null); 5 },

この書き方じゃ駄目。
requestは非同期処理だから、結果を待たずしてrequestの次の行が取得されてしまう。
つまり即callbackが叩かれて質問文通りの動きになる。

多少冗長でもこう記述する必要がある。

JavaScript

1 function A_1(callback) { 2 console.log("A_1"); 3 request(options_A1, function (err, res, body) { 4 callback_A1(err, res, body); 5 callback(err); 6 ); 7 },

おまけ
ここからのリファクタリングテクは色々あるけど、
例えばこんな手法が使える。

JavaSccript

1var async = require('async'); 2var get_item = function (name) { 3 return function (done) { 4 console.log(name); 5 request(options[name], function (err, res, body) { 6 callback[name](err, res, body); 7 done(err); 8 ); 9 } 10} 11 12async.series( 13 ["A_1", "A_2", "A_3", "A4"].map(function (name) { 14 return get_item(name) 15 }), 16 function(err){ 17 console.log('ALL Complete.'); 18 } 19);

投稿2017/11/12 16:28

miyabi-sun

総合スコア21158

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

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

aws

2017/11/12 17:07

ご回答有難う御座います。なるほどーrequestは一手間(冗長)必要なんですねー 試してみます。
KSwordOfHaste

2017/11/12 17:11

回答のんびり書いてたらmiyabi-sunさんの回答とダブってしまいました。失礼しましたー
aws

2017/11/12 17:40

いえー本当に助かりましたので同じ様な回答でも気にしていませんのでご安心下さいませ。
miyabi-sun

2017/11/12 23:33

> requestは一手間(冗長)必要なんですねー これはrequestだけの話じゃなくて、Node.jsやJavaScript共通の話だね。 シングルスレッドで動く以上、ネットワークやファイルI/Oは遅いから結果待ちの間何もできなくなっちゃう。 これを解決する為に、JSやNode.jsは予め予想される処理を沢山の関数に小分けして、 コールバック関数として渡しておくという手段を取ったんだよ。 その結果コールバック地獄というこの世の地獄が生まれちゃうわけだけどね… この辺の事情や解決方法は下記の記事を御覧ください。 https://qiita.com/gaogao_9/items/5417d01b4641357900c7
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問