

いつもお世話になっております。javascript初心者です。  
前回、ajaxのsuccess時に、さらにjsonを呼び出す方法を学んだんですが、  
今度はjson内でのカウントアップした値をjsonを抜けたあとに引き継ぎをしたいのですが、なぜかうまく行きません。

お手数おかけしますが、ご教授お願い致します。

現在の結果 → alert(j);  
alert 0  
alert 0  
alert 0  
alert 0  
alert 0  
alert 0  
alert 0  
alert 0

求める結果 → alert(j);

alert 0  
alert 1  
alert 2  
alert 3  
alert 4  
alert 5  
alert 6  
alert 7

[code]

    function Henka() {
    
        $.ajax({
            url: 'CSVのアドレス',
            success: function (era) {
    
                // csvのjson
                csvListall = $.csv()(era);
    
                // 変数jの定義
                var j = 0;
    
                //for分を追加して
                for (var i = 0; i < 264; i++) {
    
                    //各変数定義;
                    var calendarId = '';
                    var calendarId = csvListall[i][2];
                    var irank = '';
                    var irank = csvListall[i][5];
                    var iimg = '';
                    var iimg = csvListall[i][7];
                    var iname = '';
                    var iname = csvListall[i][0];
                    var itarget = '';
                    var itarget = csvListall[i][6];
    
                    var tab = '[data-today="' + j + '"]';
                    var uri = "https://www.googleapis.com/calendar/v3/calendars/" + calendarId + "/events?key=" + apikey + "&timeMin=" + timeMin + "&timeMax=" + timeMax + "&maxResults=10&orderBy=startTime&singleEvents=true";
                    var jsinfo = uri;
    
                    //問題のポイントは下記から-------------------------------------------
    
                    $.getJSON(jsinfo,
                        (function (irank, iimg, iname, tab, itarget, j) {
                            return function (data) {
                                if (data.items[0].summary != "") {
                                    //処理回数カウント
                                    j = j + 1;
                                    //処理内容
                                    vivid = '<img src="' + iimg + '" alt="empty" style="height: 150px;" class="app-button" data-target="' + itarget + '"><br>' + iname + '';
                                    $(tab).append(vivid);
                                }
                            };
                        })(irank, iimg, iname, tab, itarget, j)
                    );
                    alert(j);
    
                    //問題のポイントは上記まで-------------------------------------------
    
                }
            }
        });
    }
    
[/code]


CC BY-SA 3.0
2017-04-08T13:21:15.030
33869
2019-12-15T17:04:43.183
2017-04-08T17:37:06.610
3068
21411
question
javascript json
JSON取得時にカウントアップした変数の値をその後の処理に引き継ぎたい


`j = j + 1 ;` の部分の`j`は、無名関数（関数リテラル)の引数として関数にローカルな変数なので、  
ここでの変更は関数の呼び出し側に影響しません。

解決方法は色々あると思いますが、  
１つの方法として参照型にすることで望む動作をさせることができます。  
例えば、`var j = 0;`を次のように変更して  
`var j = { counter : 0 };`

`j = j + 1 ;`を  
`j.counter += 1;`に変更します。  
そして  
`alert(j);`は`alert(j.counter);`に変更します。


CC BY-SA 3.0
2017-04-08T13:55:40.080
33871
2017-04-08T13:55:40.080
5044
33869
answer


**回答**

おそらく下記のようにすれば、JSONのダウンロードが完了した順に`[data-
today="0"]`～`[data-t‌​oday="263"]`にimgタグが追加されていくかと思います。

`[data-today="1"]`からスタートする場合、変数jの初期値を1にしてください。  
(`var j = 1;`に変更する)

[code]

    function Henka() {
        $.ajax({
            url: 'CSVのアドレス',
            success: function (era) {
                // csvのjson
                csvListall = $.csv()(era);
    
                // 変数jの定義
                var j = 0;
    
                //for文を追加して
                for (var i = 0; i < 264; i++) {
    
                    //各変数定義
                    var calendarId = csvListall[i][2];
                    var irank      = csvListall[i][5];
                    var iimg       = csvListall[i][7];
                    var iname      = csvListall[i][0];
                    var itarget    = csvListall[i][6];
    
                    var uri = "https://www.googleapis.com/calendar/v3/calendars/" + calendarId + "/events?key=" + apikey + "&timeMin=" + timeMin + "&timeMax=" + timeMax + "&maxResults=10&orderBy=startTime&singleEvents=true";
    
                    $.getJSON(uri,
                        (function (iimg, iname, itarget) {
                            return function (data) {
                                if (data.items[0].summary !== "") {
                                    //処理内容
                                    var tab = '[data-today="' + j + '"]';
                                    var vivid = '<img src="' + iimg + '" alt="empty" style="height: 150px;" class="app-button" data-target="' + itarget + '"/><br/>' + iname;
                                    $(tab).append(vivid);
    
                                    //処理回数カウント
                                    j = j + 1;
                                }
                            };
                        })(iimg, iname, itarget)
                    );
                }
            }
        });
    }
    
[/code]

**解説**

`//処理内容`の`j`はfor文の上に定義している`var j = 0;`の値を参照します。  
なので、JSONのダウンロードが終わるたびに`//処理回数カウント`が実行されて、数値が1ずつ大きくなっていきます。

自分自身の外側に定義されている変数を参照している関数は、[クロージャ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures)と呼ばれます。

今回の例では外側に定義されている変数が`j`で、`j`を参照している`//処理内容`がある関数がクロージャです。

以下は質問にあるコードで気になった点です。

  * `var irank`等、2回定義している箇所がありますが、1回目の直後の2回目で値を代入しているので、1回目の定義は不要です。  
(変数の定義(`var`)は1度だけ行うようにした方がよいです)

  * `//処理内容`で`irank`は使用されていないので、`(function(){})()`に渡す必要はありません。
  * `vivid`はローカル変数かと思いますので、`var`を付けたほうがよいです。
  * 一般的に値の比較は`===`や`!==`を使用したほうがよいです。

[比較演算子 - JavaScript |
MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity)

**余談**

回答に書いたコードと同じような動作をする最小のコードを以下に作成しました。  
興味があればご覧ください。

[code]

    var csvListall = [
      [0, "sss", "//dummyimage.com/101x50/", "one", "target"],
      [1, "ss", "//dummyimage.com/102x50/", "two", "target"],
      [2, "s", "//dummyimage.com/103x50/", "three", "target"],
      [3, "a", "//dummyimage.com/104x50/", "four", "target"],
      [4, "b", "//dummyimage.com/105x50/", "five", "target"],
      [5, "c", "//dummyimage.com/106x50/", "six", "target"],
      [6, "sss", "//dummyimage.com/107x50/", "seven", "target"],
      [7, "ss", "//dummyimage.com/108x50/", "eight", "target"],
      [8, "s", "//dummyimage.com/109x50/", "nine", "target"],
      [9, "a", "//dummyimage.com/110x50/", "ten", "target"]
    ];
    
    var j = 0;
    
    for (var i = 0; i < 10; i++) {
      var calendarId = csvListall[i][0];
      var rank = csvListall[i][1];
      var img = csvListall[i][2];
      var name = csvListall[i][3];
      var target = csvListall[i][4];
    
      // 非同期処理 (JSONのダウンロードを想定)
      setTimeout((function(img, name, target) {
        return function(data) {
          if (Math.random() > 0.5) {
            //処理内容
            var vivid = '<img src="' + img + '" alt="empty" data-target="' + target + '"/><br/>' + name;
            $('[data-today="' + j + '"]').append(vivid);
            j++;
          }
        }
      })(img, name, target), 500);
    }
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <div data-today="0"></div>
    <div data-today="1"></div>
    <div data-today="2"></div>
    <div data-today="3"></div>
    <div data-today="4"></div>
    <div data-today="5"></div>
    <div data-today="6"></div>
    <div data-today="7"></div>
    <div data-today="8"></div>
    <div data-today="9"></div>
[/code]

0～1の値をランダムに生成する`Math.random()`の値が0.5より大きかった場合、imgタグを追加する処理が動くようになっています。


CC BY-SA 4.0
2017-04-09T13:08:54.503
33886
2019-12-15T17:04:43.183
2019-12-15T17:04:43.183
3068
3068
33869
answer


$.getJSONは非同期処理なので、コールバックが完了する前にalertが実行されてカウントが増えていかない他、コールバックが$.getJSONを実行した順番に実行されるとは限りません。そのため'data-
today'の順番が入れ替わってしまう可能性もあります。順番が異なってしまうのは好ましくないのではないかと考えますと、非同期の$.getJSONを直列で実行する必要があります。$.getJSONは$.Deferredのオブジェクトを返しますので、テストしてませんが以下の抜粋のようにすると直列され、問題が解決できると思います。なお、直列になるため処理は遅くなります。

[code]

    // 変数jの定義
    var j = 0;
    
    // **追加** Deferredを保持する変数の定義
    var dfrd;
    
    //for分を追加して
    for (var i = 0; i < 264; i++) {
    
        //各変数定義;
        ...
    
        if (! dfrd) {
            dfrd = (function() {
                var d = new $.Deferred();
                d.resolve();
                return d.promise();
            })();
        }
    
        // **問題のポイントを以下のように変更**
        dfrd.then(
            dfrd = $.getJSON(jsinfo,
                (function (irank, iimg, iname, tab, itarget, j) {
                    return function (data) {
                        if (data.items[0].summary != "") {
                            //処理回数カウント
                            j = j + 1;
                            //処理内容
                            vivid = '<img src="' + iimg + '" alt="empty" style="height: 150px;" class="app-button" data-target="' + itarget + '"><br>' + iname + '';
                            $(tab).append(vivid);
                        }
                    };
                })(irank, iimg, iname, tab, itarget, j)
            )
            .then(  // alertを消す際には、このthenも不要になります
                alert(j);
            );
        );
    }
    
[/code]


CC BY-SA 3.0
2017-11-08T11:03:31.130
39416
2017-11-08T11:03:31.130
26093
33869
answer