Ajax経由でjQueryを実行したらOutOfMemoryが返ってきた時のメモ


#追記、編集しました。
__追記・編集した内容メモ__

（20160922）
・実行コードを再現するものに修正しました。
　この記事を書いたときのは自分のテスト環境で動かしてた確認途中のものでした、、ちゃんと再現するものに差し替えました。

・__症状__ の内容を具体的に記述しました。

(20160924)
・コメントいただいた内容をもとに、追加で検証を行ってみました。
　ブラウザ2種（FireFoxとGoogleChrome）、およびindex.htmlとajax.jsにalertを仕込んだパターンとconsole.logを仕込んだパターンと、なにもいれていない場合で挙動の違いを確認してみました。

(メモここまで）



今日イチつまったのでメモしておく。

# 書いてる人間
インフラ側がメインなエンジニアでアプリケーションはまったくといっていいほどです。
勉強中なので、この記事をご覧になった方に詳しい方がいたら解説いただけますと幸いです。。

# 起きたこと
 __やろうとしていること（全体）__
HTMLのFormのボタンを押すことにより、フォームに入力された値をajax経由でphpのスクリプトに渡し、
処理結果をHTMLの特定箇所に書きだしたい。
ネット上で見かけたサンプルコードを動かしてみながら、処理の流れをふんふんと勉強中。

__環境__
 クライアント：
Windows7 64Bit　ブラウザはFireFoxのたぶん最新くらい

 サーバ:
CentOS7 apache2.4 php5.6らへん


__書いてたコード__
こんな感じ。

```HTML:index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>ajaxサンプル</title>
    </head>
    <body>
        <h1>選択</h1>
        <form id="testform" name="testform" action="" method="POST">
        <p>
            text1: <input type="text" id="text1" name="text1" value=""><br>
            text2: <input type="text" id="text2" name="text2" value=""><br>
            <select id="select" name="select" size="3">
              <option value="A" selected>This is A</option>
              <option value="B">This is B</option>
              <option value="C">This is C</option>
            </select>
            <input type="submit" id="execute" value="送信"><br>
        </p>
        </form>
        <hr>
        <h1>結果</h1>
        <div id="loading">
        </div>
        <div id="result">
        ここが置き換わる
        </div>
        <script>
        alert("ページロード時に実行されるjs");
        </script>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
        <script src="ajax.js"></script>
    </body>
</html>


```

"loading"の部分には、処理中にあのくるくるを表示するための場所。（loading.gifみたいなのをここにいれる、今回の話題とは関係ないので割愛）
"result"の部分に、phpの処理結果をhtmlで返していれたい。


```javascript:ajax.js

$(function () {
    // 「#execute」をクリックしたとき
    $('#execute').click(function () {

        //formに入力された値を配列で取得
        var formDataArray = $('#testform').serializeArray();

        //連想配列に入れなおす
        var formDataHash = {};
        for (idx in formDataArray) {
            var key   = formDataArray[idx]["name"];
            var value = formDataArray[idx]["value"];
            formDataHash[key] = value;
        }

        //resultの中身を削除
        $("#reuslt").empty();
        //loadingのgifを表示
        $("#loading").append("<p class='loading-gif'><img src='loading.gif'></p>");
        // Ajax通信を開始する
        $.ajax({
            url: 'api.php',
            type: 'post', // getかpostを指定(デフォルトは前者)
            dataType: 'json', // 「json」を指定するとresponseがJSONとしてパースされたオブジェクトになる
            data: { // 送信データを指定(getの場合は自動的にurlの後ろにクエリとして付加される)
                hash: formDataHash//formに入力された値を連想配列でPHPに渡す
            }
        })
        // ・ステータスコードは正常で、dataTypeで定義したようにパース出来たとき
        .done(function (response) {
            alert("done");
            //resultの結果を表示
            $('#result').html(response.data);
            //loadingのgifを削除
            $('.loading-gif').empty();
        })
        // ・サーバからステータスコード400以上が返ってきたとき
        // ・ステータスコードは正常だが、dataTypeで定義したようにパース出来なかったとき
        // ・通信に失敗したとき
        .fail(function (response) {
            alert("fail");
            $('#result').html('fail');
        });
    });
});


```
HTMLのフォームにある
<input type="submit" id="execute" value="送信">
が押されたら、ajax.jsで定義されている　$('#execute').click(function ()　以下が走って、
フォームの値をserialaizeAarrayで取得して、連想配列に入れなおした上でapi.phpにJSONで投げる。
返ってきた結果をdoneで受け取り、#resultにHTMLとして挿入する。
Ajaxの処理に失敗したら、.failに行き、#resultには失敗したことを示すメッセージが入る。

```php:api.php

<?php

// Content-TypeをJSONに指定する
header('Content-Type: application/json');

$hashFormData = ($_POST['hash']);

sleep(5);

$data1 = "data1:<br><table border='1'>";
foreach ($hashFormData as $key => $value) {
  $data1 .= "<tr><td>" . $key . "</td><td>" . $value . "</td></tr>";
}
$data1 .= "</table>";

$data2 = "data2:";

$data = $data1 . "<hr>" . $data2;
echo json_encode(compact('data'));
```
ようはフォームに入力された値をdata1とdata2の形に整形して(data1はHTMLのテーブルです）、
HTMLタグの\<hr>で連結して、JSON形式でajax.jsに返す。
sleep(5)は処理中であることを疑似的に再現するためのsleep（ぐるぐるの動作確認）。
※実際に動かしたいのはもっと別の処理で、あくまでこれは動作確認のサンプルです。


__症状__
というようなコードを書いてみて動かしてみたけど、どうにも動かない。
jsのいろんなところにalertをしかけてメッセージを表示しながら進めていると、ぐるぐるは表示されるが、phpのsleep(5)が動いてないっぽいことから、api.phpへの通信がしくってるのかな？という風に見えた。
何がいけないんだろうと思いながら、FireFoxのdevツールみてたら、例外エラーでOut of Memoryが赤く表示されていることに気づいた。
（追記）
FireBugのコンソールに表示されるエラー文字列は「uncaught exception: out of memory」と、
api.phpへのPOSTが失敗したみたいな赤字。
フォームのボタンを押すとajax.jsの処理は走るけれども、api.phpに行かずにindex.htmlが再度読み込まれる。
apacheのログにはajax.jsが304で返ってきてるんだけど、これは関係ないかな

__OOM?__
職業柄（というほどでもないですが）Out of Memoryといわれてぱっと思いつくのは、サーバのメモリ不足（かつかつなサーバだとMySQLが落ちる。。）。※この件では自分の勘違いです！
この程度のjsでそんなメモリ使うの？って思い、一応サーバの状態確認してみるがいうほどでもないし、/var/log/messagesにOutOfMemory的な記述もない（当然だ）。
ちょっと考えてから気づいたんだけど、FireFoxがOutOfMemoryっていってるんだから、これクライアントPC側か！！って思い、Windowsのリソースモニタ確認してみるも、こっちもそれほどでもない。たしかにFireFoxそこそこメモリ食ってましたが。

__ぐぐる__
助けてぐーぐる先生。
「ajax jQuery Out of Memory」とかそういう残念な感じでぐぐる。
javascriptのメモリ周りでGCやらなんやらの記事はでてくるけど、これっていう解がなかなか見当たらない中、StackOverFlowで以下の記事を発見。

[uncaught exception: out of memory in Ajax Process](http://stackoverflow.com/questions/29291952/uncaught-exception-out-of-memory-in-ajax-process)

わー、似たような感じだっ！てみてると、以下のような記述。

>I was having a similar problem while testing in Firefox. 
>Changing the button's type from submit to button worked for me.
>
>```HTML
><button type="submit" ...>Add comment</button>
>```
>
>to
>
>```HTML
><button type="button" ...>Add comment</button>
>```

え？そこ？って感じでしたが、上記自分のコードの
>```HTML
><input type="submit" ...>
>```
>
>を
>
>```HTML
><input type="button" ...>
>```

に変えてみたら、確かにOutOfMemoryがでなくなり、api.phpへの通信も成功するようになりました。

# なんで？
わかる方いたら教えてください。。。

__妄想1__
"submit"はすげーメモリ食うけど"botton"はそんなことない。
そんなちがいある・・・？


__妄想2__
ajaxだかHTMLだかjavascriptだかjQueryだかわからんがとにかくなんかバグがある。
でもそれだったら、普通にその情報でてきそう。


__妄想3__
OutOfMemoryと表示されていたが、実はOutOfMemoryではない（devツールの表示がオカシイ？）


。。。どれもそんなことあるー？って感じですが。。

# 参考

__コーディング関連__
[はじめてAjaxを実装する人へ、効率的な学習方法について書きました](http://javatechnology.net/ajax/biginer-start-up/)
[PHPとの間でAjax通信をおこなう](http://noumenon-th.net/programming/2016/03/29/php_ajax/)
[jQuery で Ajax を使ってみる](http://akiyoko.hatenablog.jp/entry/2014/07/21/034618)

__buttonとsubmitの違い__ (一応調べた）
[W3C](http://www.asahi-net.or.jp/%7Esd5a-ucd/rec-html401j/interact/forms.html#input-control-types)
タグとしての挙動の違いはまぁわかる。あとはまぁいろいろ過去経緯があって、ボタンタグが使えない環境があってinput typeで使い分けてたとか、いろいろあったみたい。メモリリークについてはよくわからん。。

__StackOverFlow__
[uncaught exception: out of memory in Ajax Process](http://stackoverflow.com/questions/29291952/uncaught-exception-out-of-memory-in-ajax-process)
↑にもリンクしたやつ。結果的にはこれ。まぁこっちではbuttonタグを使ってますが、typeをsubmitからbuttoに変えたら直った、という点ではこれでした。
同じ症状になった、という人はちょこちょこいるみたい？


# 追加検証

コメントで指摘いただいた内容をもとに、実際に動かしながら以下のパターンにわけて挙動を確認しました。

__確認パターン__
・FireFoxとChromeでの違い
・index.htmlにalertを入れた場合と、console.logを入れた場合と、何もいれなかった場合
・ajax.jsの.fail以下と.done以下にalertを入れた場合と、console.logを入れた場合と、何もいれなかった場合
 = 2 * 3 * 3 = 18パターン

__確認内容__
・想定する挙動になっているかどうか
・ajaxの処理はdoneかfailか
・api.phpは実行されているかどうか
・HTTPのステータスコード
・その他気になった点

__補足__
結論からいうと、input typeがsubmitならすべて失敗（想定する挙動ではない）となり、逆にbuttonならすべて成功でした。
さらにChromeだと、buttonでも2回更新すると1回はindex.htmlがHTTP412を返し、画面が真っ白になるっていう感じでした。。
ここらへんって、きっとお作法的によくないところもあるような気がするので今回の話題とは別のところで直すべきところがあるような気がしてますが、ご参考までに。

__検証結果__
ちょっと分かりずらいかもですが、、以下の通りでした。
## FireFox
input type="submit" はすべてボタン押したあとにapi.phpを呼ばずにajaxは.failに行き、さらにページがリロードされる。
console.log,alertどちらも入ってれば出力される。
ただし、ajax.jsの.failの中にいれたalertは、alertのウィンドウ表示後にすぐ消えて勝手にページがリロードされちゃう。
ajax.jsが304を返す
## GoogleChrome
input type="submit"　はすべてボタン押したあとにapi.phpが実行されるが、ページがリロードされて結果は出力されない。
※ブラウザ上の挙動はFFといっしょだが、apacheのログからapi.phpがsleep(3)を実行後にちゃんと200を返している。
また、設定によらずブラウザを更新するたびに、2回に1回（50%ぐらい、とかではなく、更新の度に交互に）、index.htmlが412を返しページが真っ白になる。
console.logとalertは入ってればでるが、FF同様にajax.jsの.failにあるalertはウィンドウ表示後すぐページがリロードされて消える。そもそも.doneにはいかない。

Chromeに関しては、そもそもinput type="butotn"でも、上に書いた2回に1回の412でページ真っ白はでるので、
なんかもっとほかのおまじない的なものが足りてないのかもしれない。。



__ここまでのまとめ__

・javascriptのデバッグ、挙動の追跡にはconsole.logを使え（すいません存在は知ってましたがよくわかってませんでした）
・ajax.jsの.doneや.failの中にalertを入れるな
・input typeはbuttonを使うか、そもそもbuttonタグを使え