[PHP x Ajax] 非同期でセッションを複数処理する場合の注意点

発生した問題点:

  • 時間のかかる非同期処理を実行 -> その処理が終了する前に画面遷移
  • 画面遷移後、セッションを更新していると、前の画面の処理が終わった瞬間、セッション情報が古くなってエラーが発生
    • どうやら、非同期処理で実行した処理が、バックで動き続け、画面遷移した後で終了したタイミングで上書きされる模様

セッションを使わずに処理を行うようにしたりとか、色々試しましたが、根本的な原因は「セッション処理は常に1つずつ」行われ、実行中はロックされるということ。

下記の記事が参考になりました。ありがとうございました。

「【ajax】複数のajax送信を同時に行ってPHPのセッションを扱うときの注意点」

「phpでajaxとsessionを使う時の注意点:なんとなしの日記」

対応策

「session_write_close」を使うこと。

  • 現在のセッションを終了し、セッションデータを書き込む
  • セッションは同時書き込みを防ぐため、スクリプトの終了まではロックされる
    • セッションの処理ができるのは、常に1つだけ
  • セッションの変更後にすぐにセッションを終了することで、ロック待ちによる遅延を防ぐことができる

過去に非同期で複数の処理を取得しようとして、遅延が発生していた件が思い当たりました…。これでしたか……。
非同期なのに、どうして遅くなるのか不思議でした。

問題のスクリプトのエラーは発生しなくなりました。
原因が分かりましたので、今後は積極的に利用していこうと思います。

jQuery で作るシングルページ・アプリケーション(SPA) :70行で動くサンプル付き

こんにちは。jQuery 大好きです。
SPAが気になるこの頃です。

AngularJS や Vue.js を時間のある時にさわって、何とか使えることが分かりホッとしていますが、もっと簡単に使えないものかな、と思っていました。
フレームワークを使うまでもなく、小規模のウェブアプリを、SPAでサクッと作れるようになりたい。

ブレイクスルーJavaScript』を読むと、フレームワークなしでSPAを作る手法が掲載されていました。

なるほど、こういうことか、と合点がいきました。
SPAは、「ハッシュタグを使って、画面遷移を実装する」と理解しました。
AngularJS でもそうでした。
調べたところ、このハッシュタグの取得は、jQuery でも、プラグインを入れれば実装できると分かりました。
※このプラグインは、最新のjQueryで使うためには、多少手を加えるところがありますが、IE6からも対応しています。心強いですね。

以下のサンプルは、ボタン押下で画面遷移を行い、最後の画面に到達したら、ブラウザの「戻る」ボタンを押しても戻れない、というSPAです。
用途として、メールフォームを想定しています。メール送信完了後に、前の画面に戻ってほしくないので。
70行で(ひとつのHTML)で、3画面を表現できます。
2画面目は、ブラウザの「戻る」ボタンで、前に戻ることもできます。

まず、事前処理として、以下のリンクから、jQuery でハッシュタグのイベントを使えるようにするためのプラグインをダウンロードします。
圧縮版でかまいません。
http://benalman.com/projects/jquery-hashchange-plugin/
次に、jQuery 1.9系、2系では、以下の変更が必要です。
「$.browser.msie &」 を 「$.support.msie &」 に改修する。これだけです。準備OK!

プラグイン導入後は、.hashchange というイベント、ハッシュタグが変更された時のイベントが使えるようになります。
ハッシュタグ自体は、location.hash で取得できますし、これに値を上書きすることができます。

$(window).hashchange( function(){
    alert( location.hash );
    console.log('ハッシュが変わりました');
});

以下が、「最後の画面(完了画面)まで進んだら、戻れない」SPAになります。

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8" />
 <title>Single Page App</title>
<style type="text/css">
.page { display: none; }
</style>
</head>
<body>
 <div id="one" class="page">
 <h1>One</h1>
 <button class="btn" data-hash="two">Twoへ</button>
 </div>
 <div id="two" class="page">
 <h1>Two</h1>
 <button class="btn" data-hash="three">Threeへ</button> 
 </div>
 <div id="three" class="page">
 <h1>Three</h1>
 </div>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.1.min.js"></script> 
<script type="text/javascript" src="jquery.ba-hashchange.min.js"></script> 
<script type="text/javascript">
$(function(){
 // 初期表示
 if( location.hash === ''){
 displayPage("#one");
 }else{
 displayPage(location.hash);
 }
 // 完了フラグ(falseになったら遷移は不可にする) 
 // 途中でURLを変更しても、falseになっていたら遷移不可
 var completeFlg = true;
 
 // ボタン押下のページ遷移処理
 // ハッシュのみ変更する
 $('.btn').click(function(e){
 e.preventDefault();
 var hash = "#"+$(this).attr('data-hash');
 location.hash = hash;
 });
 
 // ハッシュが変更されたら、そのハッシュに基づいて表示する
 $(window).hashchange(function(){
 if(completeFlg === true){
 clearPage();
 displayPage(location.hash); 
 }else{
 location.hash = "#three"; 
 return false;
 }
 if(location.hash === "#three"){
 completeFlg = false;
 }
 }); 
 
 // ページ初期化処理
 function clearPage(){
 $(".page").css("display", "none");
 }
 // ページ表示処理
 function displayPage(hash){
 $(hash).css("display", "block");
 // $(hash).fadeIn(2000, "linear"); // アニメーションをつけることも出来る
 }
}); 
</script> 
</body>
</html>

最初に、ページに相当するDIVをCSSで非表示にしています。
JSでやると、一瞬表示されてから消えるというタイムラグが発生しますので、ここはCSSに任せます。
また、最初のアクセス時には、ハッシュタグがついていないことを想定しています。

ブレイクスルーJavaScript』を読んだなら、もっとこうすればいいのに、など意見がありましたら、ぜひコメントをお願いします。
SPAの構造が理解できたら、jQuery で実装したくなり、やってみた結果が、このサンプルコードになります。
画面遷移がひとつのHTMLファイルで実現できるのは、面白いです。それに、jQuery とはいえ、結構早いのがうれしいです。

jQueryでクロージャ

JavaScriptでそのもので書けば早いかも知れませんが
jQueryを使っているサイトなら簡単に書けるので
活用しないともったいない気がします。

・最初の処理を確認するフラグを
用意するのがポイントです。

var InfoFlg = false;
$(document).on(‘click’, ‘#hoge’, function() {
    if (InfoFlg === false) {
        最初だけ実行する処理;
    }
    // フラグを立てる
    InfoFlg = true;
    // 2回目以降繰り返す処理
});

jQuery で選択された要素の属性値を取得するには

セレクトボックスの選択された値は、通常 val() で取得しますが、
option に属性を追加することで、複数の値を取得したい時があります。


たとえば title 属性を追加して、カテゴリIDをセットするとか。

この時の属性 title の値は、以下のように取得することができます。

$("#hoge").children(':selected').attr("title");

find を使う方法もありますが、直下の子要素だけに限定する場合は
children の方が良いようです。