ループ(for)内のajax通信(非同期)でうまく実行されない

2018-04-11JavaScript

私は管理画面などボタンが押された時に処理を行う際にajaxを用いて、別PHPに投げて処理を行うというのを多用しています。
タイトルに書いてあるような、非同期でやり取りをしなくてはならないということではないのですが、そこは何も考えず非同期で処理を行っていました。

今回は下のイメージのような表(JqGrid)で、選択された行を編集、削除という処理を行っていました。

JqGridを使用した管理画面

今までは、イメージにあるようなチェックボックスなどなく、単一選択、単一処理で行っていたため
特に、気にすることもなかったのですが、利用者から、複数選択して削除をしたいと・・・
そこで、選択をマルチに変更し選択した情報をループで処理したらよいかと安易に考えていました。

ちなみに、JqGridをマルチ選択への変更は次のコードを追加するだけで簡単にでき、チェックボックスも自動で追加されます。
(チェックボックスは無くてもいいのですが・・・)

("#list").jqGrid({
	~(省略)~
	multiselect  : true ,  // false: 単一選択 true: 複数選択
	grouping     : false,  // グルーピングoff
});

ajaxをループ内で実行

ループで回すだけですので次のようなソースを書きました。

// idの取得
var grid    = jQuery( "#list" );
var selrows = grid.getGridParam('selarrrow');

var chk = 0;
for (var i = 0; i < selrows.length; i++)
{
	// 選択行
	var row = grid.getRowData(selrows[i]);
	// すでに取り消されている場合はスキップ
	if ( row.tmpflghiden != '1' ) {
		$.ajax({
			type: "POST",
			url: "*********************",
			timeout: 30000,
			data: {
				// POSTのパラメータ
				~ (省略) ~
			},
			success: function(j_data){
				~ (エラー処理) ~
				chk = -1;
			},
			complete:function(j_data){
				chk = 1;
			}
		});
		// エラーが発生したら終了
		if ( chk == -1){
			break;
		}
	}
}

よしできたと思いきや、実行してみると始めの1件しか処理されません。
??
途中ログなど出してみると、確かにクルクル回っています。

ajaxをループ内で実行する際に気を付けること

色々調べてみると、今書いているajaxは『非同期』で処理しています。
非同期のajaxをループすると、思うような動作をしてくれません。
ajaxは非同期で動いている為、処理している間ループが回りきってしまうのかなと思ったりしています。

なら、どうしたらよいかというと。

// idの取得
var grid    = jQuery( "#list" );
var selrows = grid.getGridParam('selarrrow');

var chk = 0;
for (var i = 0; i < selrows.length; i++)
{
	// 選択行
	var row = grid.getRowData(selrows[i]);
	// すでに取り消されている場合はスキップ
	if ( row.tmpflghiden != '1' ) {
		function(para){
		$.ajax({
			type: "POST",
			url: "*********************",
			timeout: 30000,
			data: {
				// POSTのパラメータ
				~ (省略) ~
			},
			success: function(j_data){
				~ (エラー処理) ~
				chk = -1;
			},
			complete:function(j_data){
				chk = 1;
			}
		});
		})(i);
		// エラーが発生したら終了
		if ( chk == -1){
			break;
		}
	}
}

と、無名関数で囲むことで実現ができます。
※無名関数で囲んでしまうと、ステータス用のchk のスコープの問題がありますが、今は置いておきます。

理由はいまいち理解していないのですが、クラスと同じ考えなのかなと一人で思っています。
実現できるからいいかと・・・

と、いろいろ調べてはみましたが、よくよく考えると・・・「非同期」である必要なのか??
このシステムの運用では、そう膨大なデータを扱うわけではありませんので、単純に同期通信にしてやれば問題ないのでは?

ajaxを同期通信にする

// idの取得
var grid    = jQuery( "#list" );
var selrows = grid.getGridParam('selarrrow');

var chk = 0;
for (var i = 0; i < selrows.length; i++)
{
	// 選択行
	var row = grid.getRowData(selrows[i]);
	// すでに取り消されている場合はスキップ
	if ( row.tmpflghiden != '1' ) {
		$.ajax({
			type: "POST",
			url: "*********************",
			async: false,	// 同期通信
			timeout: 30000,

			~ (省略) ~
	}
}

結局のところ、同期通信にして解決。