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

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

ただいまの
回答率

90.51%

  • JavaScript

    16498questions

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

  • Node.js

    1872questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 2,425

aws

score 29

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のまで進んでしまいデータ保存がうまくいかないです。(正確には保存するデータが何もない。)

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

宜しくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+5

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

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

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

    function A_1(callback) {
        console.log("A_1");
        request(options_A1, function (err, res, body) {
            callback_A1(err, res, body);
            callback(err);
        );
    },

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

var async = require('async');
var get_item = function (name) {
  return function (done) {
    console.log(name);
    request(options[name], function (err, res, body) {
      callback[name](err, res, body);
      done(err);
    );
  }
}

async.series(
  ["A_1", "A_2", "A_3", "A4"].map(function (name) {
    return get_item(name)
  }),
  function(err){
    console.log('ALL Complete.');
  }
);

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/13 02:07

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

    キャンセル

  • 2017/11/13 02:11

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

    キャンセル

  • 2017/11/13 02:40

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

    キャンセル

  • 2017/11/13 08:33

    > requestは一手間(冗長)必要なんですねー
    これはrequestだけの話じゃなくて、Node.jsやJavaScript共通の話だね。
    シングルスレッドで動く以上、ネットワークやファイルI/Oは遅いから結果待ちの間何もできなくなっちゃう。

    これを解決する為に、JSやNode.jsは予め予想される処理を沢山の関数に小分けして、
    コールバック関数として渡しておくという手段を取ったんだよ。

    その結果コールバック地獄というこの世の地獄が生まれちゃうわけだけどね…
    この辺の事情や解決方法は下記の記事を御覧ください。
    https://qiita.com/gaogao_9/items/5417d01b4641357900c7

    キャンセル

+5

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は次のよう用いるべきです。

async.series([
    function (callback) {
      console.log("A_1");
      request(option_A1, function (err, result) {
        console.log("A_1 done");
        callback(err, result);
      });
    },
    ...
  ],
  function (err, result) {
    if (err) {
      console.log('error: ' + err);
    } else {
      console.log('all completed: ' + JSON.stringify(result));
    }
  });

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

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/11/13 02:37

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

    キャンセル

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

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

関連した質問

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

  • JavaScript

    16498questions

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

  • Node.js

    1872questions

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