jQueryとPHP間のAjaxで通信途中に都度出力するストリーミングについて

どうもふうやです。

最近はWordpress側と1から構築のシステムの両刀稼働しています。

そこでjQuery+PHP間のAjax通信を行っていたのですが、PHP側で処理することがまあまあ多くて、長いもので20秒くらい待機時間があったため、ユーザー体験的には最悪だなあと思っていました。

本当にロードしてくれるのか不安になる待機時間をなんとか出来ないかということで、海外フォーラムあさりまくりました笑

そこで、無事jQuery+PHP間のAjax通信で出力があれば都度配信させるストリーミングに関しての知見を得たので、備忘録代わりに残しておきます。

今回は通常の出力とPHP側で外部コマンドを叩く場合(jQuery+PHP+Ajax+外部コマンド)も含め、ニッチ過ぎますがボリューム満点な記事でお送りいたします。

jQueryとPHP間のAjax通信でストリーミング配信をする

jQuery

前提として当たり前ですが、jQueryを先に読み込んでおきましょう。

jQuery
$(".loading-img").show();
$.ajax({
  url:'./ajax.php',
  type:'POST',
  data:{
    'data':data
  },
  xhrFields:{
    onprogress:function(e){
      $('.result-box').html(e.currentTarget.responseText);
    }
  }
})
.done(function(data){
  $('.result-box').html(data);
  $(".loading-img").hide();
})
.fail(function(e){
  $(".loading-img").hide();
});

xhrFieldsを使用するみたいです。

確認済み環境としては、jQueryのver3.2.1です。

onprogressでAjaxの通信途中を監視できるみたいで、”e.currentTarget.responseText”にPHP側の出力内容が入っています。

通信が成功(done)した際に、念の為出力内容全体を”data”で”result-boxクラス”に出力しています。

またユーザー体験を少しでも向上させるために、ローディング画像などをfixedさせると良いでしょう。

PHP

PHP側は至って簡単です。

PHP(ajax.php)
if(isset($_POST["data"])){
	echo "Hello World";
	flush();
	echo "こんにちは、世界";
	flush();
}

Ajax通信中に都度出力させたいものの後に、”flush();”というPHP組み込みの魔法の関数を実行するだけです。

また注意点として、jQuery側の”e.currentTarget.responseText”で取得する出力内容はflush前の内容です。

なので、複数回flushさせる場合は、flushとflushの間いわゆる差分が”e.currentTarget.responseText”で取得できるので、変数に差分をまとめて都度echoさせる必要はありません。

ajax成功時(done)では全体の出力内容をdataで取得することが可能です。

PHPで外部コマンド(番外編)

海外フォーラムで見つけた処理ですが、どこで見つけたのか覚えていないので引用できなくてごめんなさい!

PHP
//exec-streaming
function realtime_exec($cmd){
	passthru($cmd);
	while(@ob_end_flush());
	
	$proc = popen($cmd,'r');
	while(!feof($proc)){
	    echo fread($proc, 1024);
	    @flush();
	}
}

realtime_exec関数の引数には、実行するコマンドを渡してあげます。

すると、このコードの場合1024B(1KB)毎にAjax側の”e.currentTarget.responseText”に差分を投げてくれます。

また、外部コマンドでPythonを叩いた場合は、Python側でflushは必要なくprintでOKです。

以上になります。

まとめ

jQuery+PHP間で5秒以上(特に10秒以上笑)の通信をするようなことはあまり無いと思いますが、ユーザー体験を最適化するために出力内容があれば都度配信(ストリーミング)も実装すると良いですね!

ニッチ過ぎる記事にはなりましたが、刺さる方に刺さればそれだけで満足です。

ありがとうございました!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です