背景
Laravelのフラッシュデータを使うと、そのリクエスト間だけセッションにデータを保持できますが
これをAjax通信などで利用すると、他のXHRが干渉してフラッシュデータが破棄されてしまう可能性があります。
回避策としてLaravelはreflash()
やkeep()
という機能を提供していますが、制約的に実現が難しかったので
「Ajax通信が全て終わってからリクエストできるようにすればいいんじゃないか?」と思い調べた結果を共有します。
どうやるか
((open) => {
const openedXhr = [];
XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {
this.addEventListener('readystatechange', () => {
switch (this.readyState) {
case 1: // OPENED
openedXhr.push(this);
break;
case 4: // DONE
const i = openedXhr.indexOf(this);
if (i > -1)
openedXhr.splice(i, 1);
if (!openedXhr.length)
// フラッシュデータを使うAPIを叩く
break;
}
});
open.call(this, method, url, async, user, pass);
};
})(XMLHttpRequest.prototype.open);
Ajaxで使われるXMLHttpRequest.prototype.open
をインターセプトしてreadystatechange
を監視します。
ステータスが全てDONE
になったら全てのXHRが完了していることを保証できるため、フラッシュデータを使う処理を実行します。
今回のようなユースケースに限らず、Ajax通信が全て完了したことをトリガーにした処理を実装したい時に使えると思います。
なおこの即時関数が呼び出される前に実行されたXHRは当然制御できませんが、ユーザーが内部APIをコールするようなアクションを制御したいユースケースであればある程度は制御可能かと思います。
他の懸念点としては
- 例えば1件目のXHRが2件目を監視する前にDONEになるとAPIが呼ばれてしまう
- XHRの件数が多いとユーザーを待たせる可能性もある
- 一定間隔でAjax通信しているようなケースだと更に制御が必要になったりする
などいろいろあります(参考程度にお読みいただければ幸いです)。
備考
この記事は以前Stack Overflowで見かけた内容を元にしているのですが、記事を見失ったためメモ代わりに投稿しました...