バックグラウンド処理
Background Sync(オフライン送信の後送)とPeriodic Background Sync(定期更新)。 どちらも Chromium 系のみ対応。Safari は未対応。
Background Sync
ユーザーがオフラインで送信したアクションを、オンラインに復帰した時に SW が裏で送る。 タブが閉じていても OS のバックグラウンド機構で動く。
登録
// メイン側
async function queueOutbox() {
const reg = await navigator.serviceWorker.ready
if ("sync" in reg) {
await reg.sync.register("flush-outbox")
} else {
flushNow() // フォールバック
}
}
SW 側で受け取る
self.addEventListener("sync", (e) => {
if (e.tag === "flush-outbox") {
e.waitUntil(flushOutboxFromIDB())
}
})
async function flushOutboxFromIDB() {
const items = await db.getAll("outbox")
for (const item of items) {
try {
await fetch("/api/save", {
method: "POST",
body: JSON.stringify(item.data),
})
await db.delete("outbox", item.id)
} catch {
throw new Error("retry later") // sync が再登録される
}
}
}
挙動の特徴
- イベント中に例外を投げる/Promise が reject するとOS が後で再試行
- 再試行回数とタイミングは OS 任せ
- 長時間の処理は不可。短く済む単位に分ける
Periodic Background Sync
定期的にバックグラウンドで動く。Chromium のみ。navigator.permissions で許可申請が必要で、
ユーザーがアプリをよく使う頻度に応じてOS が間隔を決める(最短12時間程度)。
// 許可確認
const status = await navigator.permissions.query({
name: "periodic-background-sync",
})
if (status.state === "granted") {
const reg = await navigator.serviceWorker.ready
await reg.periodicSync.register("update-content", {
minInterval: 24 * 60 * 60 * 1000, // 24時間(最短のヒント)
})
}
// SW 側
self.addEventListener("periodicsync", (e) => {
if (e.tag === "update-content") {
e.waitUntil(updateContent())
}
})
ニュースアプリの記事先読み、メールのバックグラウンド受信などに。 制約が多いのでメインの仕組みにせず補助的に使う。
Background Fetch
大きなファイルのダウンロード(動画・モデルファイル等)をバックグラウンドで継続させる仕組み。 OS のダウンロードマネージャに似た UI が出る。Chromium のみ。
const reg = await navigator.serviceWorker.ready
const bgFetch = await reg.backgroundFetch.fetch(
"movie-download",
["/movie/01.mp4", "/movie/01-subs.vtt"],
{
title: "映画01",
icons: [{ src: "/icon.png", sizes: "192x192", type: "image/png" }],
downloadTotal: 60 * 1024 * 1024,
},
)
// SW 側
self.addEventListener("backgroundfetchsuccess", async (e) => {
const cache = await caches.open("movies")
for (const record of e.registration.matchAll()) {
const response = await record.responseReady
await cache.put(record.request, response)
}
})
Wake Lock API(補助)
画面をスリープさせない仕組み。動画再生・地図ナビ等で使う。
let lock = null
async function keepAwake() {
try {
lock = await navigator.wakeLock.request("screen")
lock.addEventListener("release", () => console.log("released"))
} catch (e) {
console.error(e)
}
}
// 解除
await lock?.release()
Page Visibility
タブがバックグラウンドにあるかを検知。
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
pauseAnimations()
} else {
resumeAnimations()
}
})
BroadcastChannel
同じ origin の複数タブ・SW 間でメッセージ。同期処理に。
const channel = new BroadcastChannel("app")
channel.postMessage({ type: "DATA_UPDATED", payload: ... })
// 別のタブ / SW
channel.addEventListener("message", (e) => { ... })
Notification Triggers(実験的)
将来的にローカルでスケジュール可能な通知が来る予定(時刻指定で SW 起動)。 現時点ではサポートが限定的。
使い分け
| やりたいこと | 使う API |
|---|---|
| オフライン中の送信を後送 | Background Sync |
| 定期的にコンテンツ更新 | Periodic Background Sync |
| 大ファイルのダウンロード | Background Fetch |
| サーバから即時通知 | Web Push |
| スリープさせない | Wake Lock |
| タブ間通信 | BroadcastChannel |
Chromium(Chrome / Edge / Brave / Samsung Internet)が最も進んでいる。
Safari は Sync 系・Background Fetch を未対応。
Firefox も限定的。
PWA を Safari 中心で考えるなら、これらに依存しない設計を。