【jQuery】for ループ内で Ajax で取得した値がうまく表示できない

##実装したい機能

特定のクラスを持つエレメントから商品IDを取得し
それぞれの商品詳細ページからカテゴリを取得して
対応する商品IDを持つエレメントに表示する。

##使用するコード

```text:開発環境
jquery > 1.7.2
```

####表示するページ
```html:index.html
...
<ul>
 <li class="products pid_111">
  <p>商品名①</p>
  <p class="cate"><!--カテゴリを表示したい--></p>
 </li>
 <li class="products pid_222">
  <p>商品名②</p>
  <p class="cate"><!--カテゴリを表示したい--></p>
 </li>
 <li class="products pid_333">
  <p>商品名③</p>
  <p class="cate"><!--カテゴリを表示したい--></p>
 </li>
</ul>
...
```

####商品詳細ページ
```html:111.html
...
<div>
 <p>商品名①</p>
 <p class="p_cate">カテゴリ①</p><!--これを取得したい-->
</div>
...
```

##書いてみたコード

まず、自分の知識で書いてみました。

```javascript:product.js
var id_name = $('.products');
       
for (var i = 0; i < $(id_name).length; i++) {

   var id_sp = id_name.eq(i).attr('class').split(" ")[1];
   var id = id_sp.substring(id_sp.indexOf("_")+1,id_sp.length);
       
   (function(i){
      $.ajax({
         url: id + '.html',
         type: 'GET',
         dataType: 'html',
      })
      .done(function (data) {
         $('.' + id_sp).html($(data).find('.p_cate').text());
       })
    })(i);
}
```

####実行結果

```html:index.html
<ul>
 <li class="products pid_111">
  <p>商品名①</p>
  <p class="cate"></p>
 </li>
 <li class="products pid_222">
  <p>商品名②</p>
  <p class="cate"></p>
 </li>
 <li class="products pid_333">
  <p>商品名③</p>
  <p class="cate">カテゴリ③</p>
 </li>
</ul>
```

最後の商品にだけカテゴリが入ってしまいます。

####コンソール実行

```js:product.js
...
.done(function (data) {
   $('.' + id_sp).html($(data).find('.p_cate').text());
   console.log(data + ':' + id_sp);
})
/*
=> 結果
p_333:カテゴリ①
p_333:カテゴリ②
p_333:カテゴリ③
*/
```

ajax自体は正常に動いている様子です。
ループも商品の個数分回っています。
しかし、商品IDがすべて最後の商品のIDになっています。

####考察
ajax処理が完了する前にループが回りきってしまうため
商品IDを取得するタイミングと商品カテゴリを取得するタイミングにずれがある？

##追記
@netebakari 様にコメントを頂き
そもそも「クロージャの性質」によるものだとわかりました。
（コメント参照）

forループ内の1つ目の変数
`var id_name = $('.products');`を
`const`か`let`で宣言することで
forループ内でも変数（定数）を維持することができて
適応した商品IDを取得することができました。

####動くコード

```javascript:product.js
var id_name = $('.products');

for (var i = 0; i < $(id_name).length; i++) {
   const id_sp = id_name.eq(i).attr('class').split(" ")[1]; // var→constへ変更
   var id = id_sp.substring(id_sp.indexOf("_")+1,id_sp.length);

   $.ajax({
       url: id + '.html',
       type: 'GET',
       dataType: 'html',
   })
   .done(function (data) {
       $('.' + id_sp).html($(data).find('.p_cate').text());
   });
}
```

**コンソールを実行**

```js:product.js
...
.done(function (data) {
   $('.' + id_sp).html($(data).find('.p_cate').text());
   console.log(data + ':' + id_sp);
})
/*
=> 結果
p_111:カテゴリ①
p_222:カテゴリ②
p_333:カテゴリ③
*/
```

今度こそ、想定の動きをしてくれました！


## ~~対策を調べる~~
~~グーグル先生に聞いてみたところ、大きく３つの対応策がありました。~~

- ~~ajaxを非同期にする~~
 - ~~ソース：https://it-engineer-info.com/language/javascript/1123/~~
- ~~無名関数で囲う~~
 - ~~ソース：http://web-trash.info/2017/01/30/jquery-loop-in-ajax/~~
- ~~取得した値を配列に含んでeachで吐き出し~~
 - ~~ソースはありませんが、teratailのコメントで発見しました。~~

#### ~~ajaxを非同期にする~~
~~こちらは特に変化なしです。
これが原因ではないようです。~~

#### ~~無名関数で囲う~~
~~すでに囲っていました。
これも原因ではないようです。~~

#### ~~取得した値を配列に含んでeachで吐き出し~~
~~もう、これしかありません。
頑張って書いてみます。~~

```javascript:product.js
var id_name = $('.products');
var arr = [  ];// <- 追加
       
for (var i = 0; i < $(id_name).length; i++) {

   var id_sp = id_name.eq(i).attr('class').split(" ")[1];
   var id = id_sp.substring(id_sp.indexOf("_")+1,id_sp.length);
       
   (function(i){
      $.ajax({
         url: id + '.html',
         type: 'GET',
         dataType: 'html',
      })
      .done(function (data) {
         var cate = $(data).find('.p_cate').text();// <- 変更
         arr.push( {p_id:id_sp, cate_name:cate} );
       })
   })(i);

   $.each(arr, function(index, value) { // <- 追加
       var find = '.' + value.p_id
       var cate_in = value.cate_name
       $(find).html(cate_in);
   });

}
```

#### ~~コンソール実行~~

```js:product.js
...
$.each(arr, function(index, value) {
   var find = '.' + value.p_id
   var cate_in = value.cate_name
   $(find).html(cate_in);
   console.log(find + ':' + cate_in);
});
/*
=> 結果
p_111:カテゴリ①
p_222:カテゴリ②
p_333:カテゴリ③
*/
```

~~うまく回っています！
商品IDとカテゴリが適合しています！~~

#### ~~実行~~
```html:index.html
<ul>
 <li class="products pid_111">
  <p>商品名①</p>
  <p class="cate">カテゴリ①</p>
 </li>
 <li class="products pid_222">
  <p>商品名②</p>
  <p class="cate">カテゴリ②</p>
 </li>
 <li class="products pid_333">
  <p>商品名③</p>
  <p class="cate">カテゴリ③</p>
 </li>
</ul>
```

しっかりと表示されました！
一安心です…笑

##まとめ
一見単純な動きでも、ループや非同期通信が絡み合うと想定した動きにならないということを体感できました。
取得したデータを配列に格納して吐き出す、という考え方も知ることができました。
思考の幅がぐっと広がりました！

初学者が手探りで書いたコードなので、間違っている可能性も高いです。
もっと合理的な書き方をご存知の方は是非コメントを下さい！