Javascript
複数タブでポーリングする方法
はち
公開日:2021/10/27
皆様こんにちはゴスケです。
最近、社内システムで「新着情報をポーリングする処理」の実装機会がありました。
なんも考えずsetTimeout()で新着情報を取得するAPIをコールしていたのですが、開いているタブの数だけリクエストが飛んでいることに気が付きました。
これはよろしくない。
できればリクエストを1つにまとめて、取得した情報を各タブに通知する仕組みを実装したいところです。
今回はその仕組を「SharedWorker + BroadcastChannel」を使用して実装してみたので紹介します。
(フロントエンドはReactを使っています。)
参考にさせていただいた記事はこちらです。
https://qiita.com/okumurakengo/items/0273d80628e021537cbc
SharedWorkerを使って複数タブのリクエストを1つにまとめる
SharedWorkerの使い方は以下の通り。
コンストラクタの引数に実行したいスクリプトを指定します。
その後start() を実行することで、SharedWorkerが使用できます。
1 2 3 4 5 6 7 8 9 10 11 |
//app.jsx const App: React.FC = () => { const myWorker = new SharedWorker("./js/workers/shared.js"); myWorker.port.start(); return (<p>Hello World</p>) } if (document.getElementById('app')) { ReactDOM.render(<App />, document.getElementById('app')); } |
続いてSharedWorkerで実行するスクリプトの実装です。
ここでは「ポーリングで新着情報を取得するAPIをコール」という処理を実装します。
今回は簡易的にするために単純なカウントアップを繰り返すだけにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//shared.js let count = 0; function polling() { try { const response = { count: count++, time: new Date() }; console.log(response); } catch (e) { console.error(e); } finally { setTimeout(polling, 3000); } } let isPoll = false; onconnect = function (e) { if (!isPoll) { polling(); isPoll = true; } } |
SharedWorker内で実行されるconsole.log()は「chrome://inspect/#workers」で確認できます。
BroadcastChannelで各タブに通知する
次にSharedWorker内でカウントアップされた数値をフロントエンドに通知する方法です。
こちらはBroadcastChannelを使用します。
まずはアプリケーション側の変更。
コンストラクタにチャンネル名を指定し、このチャンネル宛に送信されたメッセージを購読できるようになります。
今回は「shared」という名前にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//app.jsx const App: React.FC = () => { const myWorker = new SharedWorker("./js/workers/shared.js"); myWorker.port.start(); const bc = new BroadcastChannel("shared"); useEffect(() => { bc.onmessage = (e) => { console.log(e.data); } }, [bc]); return (<p>Hello World</p>) } if (document.getElementById('app')) { ReactDOM.render(<App />, document.getElementById('app')); } |
続いて、SharedWorkerの変更。
カウントアップした内容を「shared」に送信することで、「shared」を購読しているフロントエンドに通知されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//shared.js const bc = new BroadcastChannel("shared"); let count = 0; async function polling() { try { const response = { count: count++, time: new Date() }; bc.postMessage(response); bc.close(); } catch (e) { console.error(e); } finally { setTimeout(polling, 3000); } } let isPoll = false; onconnect = function (e) { if (!isPoll) { polling(); isPoll = true; } } |
最後に
今回はSharedWorker + BroadcastChannnelで、複数タブでのポーリングを実現してみました。
WebWorkerは全然使ったことがなかったので、良い経験になりました!