Service Worker内でsetIntervalを使ってポーリング処理を実装しています。タブがアクティブなときは正常に動作しますが、非アクティブ(別タブや別ウィンドウに切り替え)にすると止まってしまいます。
奇妙な挙動
DevToolsを別ウィンドウで開いて監視していると、タブが非アクティブでもポーリングが継続します。DevToolsを閉じると止まります。
コード
sw.js
javascript
1var timer = null; 2var baseId = null; 3var apiUrl = null; 4var interval = 60000; 5 6self.addEventListener('message', function(e) { 7 var data = e.data; 8 if (data.type === 'start') { 9 apiUrl = data.apiUrl; 10 interval = data.interval; 11 if (data.baseId !== null && data.baseId !== undefined) baseId = data.baseId; 12 startPolling(); 13 } 14 if (data.type === 'stop') { 15 stopPolling(); 16 } 17}); 18 19function startPolling() { 20 stopPolling(); 21 poll(); 22 timer = setInterval(poll, interval); 23} 24 25function stopPolling() { 26 if (timer) { clearInterval(timer); timer = null; } 27} 28 29function poll() { 30 fetch(apiUrl) 31 .then(function(r) { return r.json(); }) 32 .then(function(data) { 33 if (baseId === null) { baseId = data.id; return; } 34 if (baseId !== null && data.id > baseId) { 35 stopPolling(); 36 self.clients.matchAll({ includeUncontrolled: true, type: 'window' }).then(function(clients) { 37 clients.forEach(function(client) { 38 client.postMessage({ type: 'new_posts' }); 39 }); 40 }); 41 } 42 }) 43 .catch(function(e) {}); 44}
ページ側(layout.html)
javascript
1navigator.serviceWorker.register('/sw.js').then(function(reg) { 2 // 登録 3}); 4 5navigator.serviceWorker.ready.then(function(reg) { 6 sw = reg.active; 7 if (sw) sw.postMessage({ type: 'start', apiUrl: apiUrl, interval: interval, baseId: baseId }); 8}); 9 10navigator.serviceWorker.addEventListener('message', function(e) { 11 if (e.data.type === 'new_posts') showNotification(); 12});
環境
- Chrome(最新)
- HTTPS環境
- Service WorkerのスコープはサイトルートURL(
/sw.jsをルートに配置)
質問
- DevToolsを開いているときだけ動く原因はなんでしょうか?
- Service Worker内のsetIntervalをタブ非アクティブ時も動かし続ける方法はありますか?
- Push API以外で実現する方法はありますか?
最終的にやりたいのはYahoo!メールのように、タブが非アクティブでもタブのアイコンにバッジが付く挙動です。
通知許可はしていない状態です。現在はService Worker内でsetIntervalを使ってポーリングし、新着を検知したらcanvasでファビコンに色丸を追加する実装をしています。タブがアクティブなときは動作しますが、非アクティブだと止まってしまいます。