掲題の通りなのですが、ブラウザやタブを閉じるタイミングや画面内で別コンテンツに切り替える場合に
その時の状態のデータをWebAPIリクエストしたりしたい場合ってありますよね?
JavaScriptのAjax通信なんかは基本非同期なので、これをそのままブラウザやタブを閉じるタイミングで行うと、
あれ?処理されてないんだけど何で?ってなるかと思います。
これはブラウザやタブを閉じる際にunload
というイベントが発生するんですが、
その中で非同期処理を行うと非同期処理が完了する前にページが破棄されてしまうので非同期処理がキャンセルされてしまうためです。
unload
イベントについてはこちら
これを解決してくれるのがsendBeacon
というJavaScriptAPIです!
sendBeaconの仕様については以下を参照してください。
使い方はaddEventListenerのコールバック関数内でsendBeaconを呼び出してあげればよいです。
const handleUnload = () => { const body = new FormData(); body.append('_method', 'PUT'); body.append('csrf-token', csrfToken); body.append('key', value); navigator.sendBeacon(`${apiUrl}`, body); }; addEventListener('unload', handleUnload, false);
大まかにメリット / デメリットを挙げると以下です。
メリット
デメリット
- 送信データサイズに限りがある
- ヘッダー設定できない
- POSTリクエストしか投げられない
ここで補足しておくとsendBeaconはunload
時などに非同期のAPIリクエストを非同期で行えることを可能にしてくれます。
sendBeaconを使わなくてもunload
時などのタイミングでAPIリクエストを投げることはできます。
非同期での処理が行なえないだけなのでAPIリクエストを同期的に行えばできなくはないのですが、
同期的に処理を行うことでページ読み込み時に読み込み処理を一時的にブロックしてしまうため、
ここはトレードオフです。
また、私がsendBeaconを検証したときにいくつか感じた疑問や発生した課題について共有します。
送信したいリクエストのメソッドがPOST以外の場合
例えばRailsのmultipart/form-data送信仕様でformdataのパラメータに_method=PUTを持たせることでフォーム送信でPUTリクエストを投げることが可能ですがこちら試してもうまくいきませんでした。。
Json送れるのか問題
Blob使えば送れるみたいです。
const handleUnload = () => { // JSONで送信する場合 const body = new Blob( [ JSON.stringify({ key: value, }), ], { type: 'application/json' } ); navigator.sendBeacon(`${apiUrl}`, body); }; addEventListener('unload', handleUnload, false);
CSRFトークンなどの情報を渡せるか問題
JSONやFormDataでキーを追加すれば可能
sendBeaconでリクエストを投げることは確認できたけどpending状態となっているけど大丈夫か問題
想定の挙動みたいです。
sendBeaconはページのアンロード時に非同期処理の実行を保証してくれるが、その後ページが破棄されレスポンスを受け取ることができないのでpending状態となっているそうだ。
devtoolからsendBeaconでのAPIリクエストを確認するには?
Networkタブの「xhr」で確認したところ表示されていなかったが「その他」に切り替えたら表示された。
そのため、あれ?表示されてないなと思ったら「その他」に切り替えて確認してみてください。