ウェブサーバーにデータを送信すると、リクエストが失敗することがあります。原因としては、ユーザーの接続が切断された場合や、サーバーがダウンしていることなどが考えられます。いずれの場合も、後でもう一度リクエストを送信してみることをおすすめします。
新しい BackgroundSync API は、この問題に対する理想的なソリューションです。Service Worker は、ネットワーク リクエストの失敗を検出すると、sync
イベントを受信するよう登録できます。このイベントは、接続が回復したとブラウザが認識したときに配信されます。同期イベントは、ユーザーがアプリケーションを離れた場合でも配信できるため、失敗したリクエストを再試行する従来の方法よりもはるかに効果的です。
Workbox Background Sync は、BackgroundSync API を簡単に使用し、その使用方法を他の Workbox モジュールと統合できるように設計されています。また、BackgroundSync をまだ実装していないブラウザ用のフォールバック戦略も実装しています。
BackgroundSync API をサポートするブラウザは、ブラウザが管理する間隔で、失敗したリクエストを自動的にリプレイします。その際、リプレイ試行間で指数バックオフが使用される可能性があります。BackgroundSync API をネイティブにサポートしていないブラウザでは、Service Worker が起動するたびに Workbox Background Sync が自動的にリプレイを試行します。
基本的な使用法
バックグラウンド同期を使用する最も簡単な方法は、失敗したリクエストを自動的にキューに入れ、将来の sync
イベントが発生したときに再試行する Plugin
を使用することです。
import {BackgroundSyncPlugin} from 'workbox-background-sync';
import {registerRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';
const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {
maxRetentionTime: 24 * 60, // Retry for max of 24 Hours (specified in minutes)
});
registerRoute(
/\/api\/.*\/*.json/,
new NetworkOnly({
plugins: [bgSyncPlugin],
}),
'POST'
);
BackgroundSyncPlugin
は fetchDidFail
プラグイン コールバックにフックし、fetchDidFail
は例外がスローされた場合にのみ呼び出されます。これは通常、ネットワーク障害が原因です。つまり、4xx
または 5xx
エラー ステータスのレスポンスが受信された場合、リクエストは再試行されません。5xx
ステータスなどの原因となったすべてのリクエストを再試行する場合は、戦略に fetchDidSucceed
プラグインを追加します。
const statusPlugin = {
fetchDidSucceed: ({response}) => {
if (response.status >= 500) {
// Throwing anything here will trigger fetchDidFail.
throw new Error('Server error.');
}
// If it's not 5xx, use the response as-is.
return response;
},
};
// Add statusPlugin to the plugins array in your strategy.
高度な使用方法
Workbox Background Sync には Queue
クラスも用意されています。このクラスに、失敗したリクエストをインスタンス化して追加できます。失敗したリクエストは IndexedDB に保存され、ブラウザが接続が復元されたと判断したとき(同期イベントを受信したときなど)に再試行されます。
キューを作成する
ワークボックス バックグラウンド同期キューを作成するには、キュー名(送信元に固有の名前にする必要があります)を使ってキューを作成する必要があります。
import {Queue} from 'workbox-background-sync';
const queue = new Queue('myQueueName');
キュー名は、グローバル SyncManager
によって register()
を取得するタグ名の一部として使用されます。また、IndexedDB データベースのオブジェクト ストア名としても使用されます。
リクエストをキューに追加する
Queue インスタンスを作成したら、失敗したリクエストをインスタンスに追加できます。失敗したリクエストを追加するには、.pushRequest()
メソッドを呼び出します。たとえば、次のコードは失敗したリクエストをキャッチしてキューに追加します。
import {Queue} from 'workbox-background-sync';
const queue = new Queue('myQueueName');
self.addEventListener('fetch', event => {
// Add in your own criteria here to return early if this
// isn't a request that should use background sync.
if (event.request.method !== 'POST') {
return;
}
const bgSyncLogic = async () => {
try {
const response = await fetch(event.request.clone());
return response;
} catch (error) {
await queue.pushRequest({request: event.request});
return error;
}
};
event.respondWith(bgSyncLogic());
});
リクエストがキューに追加されると、Service Worker が sync
イベントを受信すると(ブラウザが接続が回復したと認識したときに)、自動的に再試行されます。BackgroundSync API をサポートしていないブラウザは、Service Worker が起動されるたびにキューを再試行します。この場合、Service Worker を制御するページが実行される必要があるため、効果は期待できません。
ワークボックスのバックグラウンド同期のテスト
残念ながら、BackgroundSync のテストはやや直感的ではなく、さまざまな理由から困難です。
実装をテストする最善の方法は、以下を行うことです。
- ページを読み込み、Service Worker を登録します。
- パソコンのネットワークをオフにするか、ウェブサーバーをオフにします。
- Chrome DevTools をオフラインで使用しないでください。DevTools のオフライン チェックボックスは、ページからのリクエストにのみ影響します。Service Worker のリクエストは引き続き通過します。
- Workbox バックグラウンド同期を使用してキューに追加するネットワーク リクエストを作成します。
- リクエストがキューに追加されたかどうかは、
Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests
で確認できます。
- リクエストがキューに追加されたかどうかは、
- ネットワークまたはウェブサーバーをオンにします。
早期
sync
イベントを強制するには、Chrome DevTools > Application > Service Workers
に移動し、workbox-background-sync:<your queue name>
のタグ名(<your queue name>
は設定したキューの名前)を入力して [Sync] ボタンをクリックします。失敗したリクエストに対してネットワーク リクエストが処理され、リクエストが正常にリプレイされたため、IndexedDB データは空になっているはずです。
型
BackgroundSyncPlugin
fetchDidFail
ライフサイクル コールバックを実装するクラス。これにより、失敗したリクエストをバックグラウンド同期キューに簡単に追加できます。
プロパティ
-
コンストラクタ
void
constructor
関数は次のようになります。(name: string, options?: QueueOptions) => {...}
-
name
文字列
パラメータの詳細については、
workbox-background-sync.Queue
のドキュメントをご覧ください。 -
オプション
QueueOptions 省略可
-
Queue
失敗したリクエストを IndexedDB に保存し、後で再試行することを管理するクラス。保存と再生のプロセスはすべて、コールバックを介して監視できます。
プロパティ
-
コンストラクタ
void
指定されたオプションで Queue のインスタンスを作成します。
constructor
関数は次のようになります。(name: string, options?: QueueOptions) => {...}
-
name
文字列
このキューの一意の名前。この名前は、このインスタンスに固有の IndexedDB に同期イベントを登録し、リクエストを保存するために使用されるため、一意である必要があります。重複する名前が検出されると、エラーがスローされます。
-
オプション
QueueOptions 省略可
-
戻り値
-
-
name
文字列
-
getAll
void
期限切れになっていないすべてのエントリを(
maxRetentionTime
ごとに)返します。期限切れのエントリはキューから削除されます。getAll
関数は次のようになります。() => {...}
-
戻り値
Promise<QueueEntry[]>
-
-
popRequest
void
キュー内の最後のリクエスト(タイムスタンプ、メタデータを含む)を削除して返します。返されるオブジェクトの形式は
{request, timestamp, metadata}
です。popRequest
関数は次のようになります。() => {...}
-
戻り値
Promise<QueueEntry>
-
-
pushRequest
void
渡されたリクエストを IndexedDB のキューの最後に(タイムスタンプとメタデータとともに)保存します。
pushRequest
関数は次のようになります。(entry: QueueEntry) => {...}
-
必要事項を入力します。
QueueEntry
-
戻り値
Promise<void>
-
-
registerSync
void
このインスタンスに固有のタグを使用して同期イベントを登録します。
registerSync
関数は次のようになります。() => {...}
-
戻り値
Promise<void>
-
-
replayRequests
void
キュー内の各リクエストをループし、再取得を試みます。再取得に失敗したリクエストは、キュー内の同じ位置に戻されます(次の同期イベントの再試行が記録されます)。
replayRequests
関数は次のようになります。() => {...}
-
戻り値
Promise<void>
-
-
shiftRequest
void
キュー内の最初のリクエストとそのタイムスタンプおよびメタデータを削除して返します。返されるオブジェクトの形式は
{request, timestamp, metadata}
です。shiftRequest
関数は次のようになります。() => {...}
-
戻り値
Promise<QueueEntry>
-
-
サイズ
void
キュー内にあるエントリの数を返します。この数には、期限切れのエントリ(
maxRetentionTime
あたり)も含まれます。size
関数は次のようになります。() => {...}
-
戻り値
Promise<数値>
-
-
unshiftRequest
void
渡されたリクエストを IndexedDB のタイムスタンプとメタデータとともにキューの先頭に保存します。
unshiftRequest
関数は次のようになります。(entry: QueueEntry) => {...}
-
必要事項を入力します。
QueueEntry
-
戻り値
Promise<void>
-
QueueOptions
プロパティ
-
forceSyncFallback
ブール値(省略可)
-
maxRetentionTime
number(省略可)
-
onSync
OnSyncCallback 省略可
QueueStore
IndexedDB のキューからの格納リクエストを管理するクラス。アクセスしやすくするためにキュー名でインデックス付けされます。
ほとんどのデベロッパーは、このクラスに直接アクセスする必要はありません。高度なユースケースのために用意されています。
プロパティ
-
コンストラクタ
void
このインスタンスをキュー インスタンスに関連付けます。これにより、追加されたエントリをキュー名で識別できるようになります。
constructor
関数は次のようになります。(queueName: string) => {...}
-
queueName
文字列
-
戻り値
-
-
deleteEntry
void
指定された ID のエントリを削除します。
警告: このメソッドでは、削除されたエントリがこのキューに属していること(つまり、
queueName
と一致すること)は保証されません。ただし、このクラスは公開されていないため、この制限は許容されます。さらにチェックすると、このメソッドが必要以上に遅くなります。deleteEntry
関数は次のようになります。(id: number) => {...}
-
id
数値
-
戻り値
Promise<void>
-
-
getAll
void
queueName
に一致するストア内のすべてのエントリを返します。getAll
関数は次のようになります。() => {...}
-
戻り値
Promise<QueueStoreEntry[]>
-
-
popEntry
void
queueName
に一致するキュー内の最後のエントリを削除して返します。popEntry
関数は次のようになります。() => {...}
-
戻り値
Promise<QueueStoreEntry>
-
-
pushEntry
void
キューの最後にエントリを追加します。
pushEntry
関数は次のようになります。(entry: UnidentifiedQueueStoreEntry) => {...}
-
必要事項を入力します。
UnidentifiedQueueStoreEntry
-
戻り値
Promise<void>
-
-
shiftEntry
void
queueName
に一致するキュー内の最初のエントリを削除して返します。shiftEntry
関数は次のようになります。() => {...}
-
戻り値
Promise<QueueStoreEntry>
-
-
サイズ
void
queueName
に一致するストア内のエントリ数を返します。size
関数は次のようになります。() => {...}
-
戻り値
Promise<数値>
-
-
unshiftEntry
void
キューの先頭にエントリを追加します。
unshiftEntry
関数は次のようになります。(entry: UnidentifiedQueueStoreEntry) => {...}
-
必要事項を入力します。
UnidentifiedQueueStoreEntry
-
戻り値
Promise<void>
-
StorableRequest
リクエストを IndexedDB に保存できるように、リクエストのシリアル化 / シリアル化解除を容易にするクラス。
ほとんどのデベロッパーは、このクラスに直接アクセスする必要はありません。高度なユースケースのために用意されています。
プロパティ
-
コンストラクタ
void
Request
の作成に使用できるリクエスト データのオブジェクトを受け入れますが、IndexedDB に保存することもできます。constructor
関数は次のようになります。(requestData: RequestData) => {...}
-
requestData
RequestData
url
と、[requestInit]https://fetch.spec.whatwg.org/#requestinit
の関連プロパティを含むリクエスト データのオブジェクト。
-
-
clone
void
インスタンスのディープ クローンを作成して返します。
clone
関数は次のようになります。() => {...}
-
toObject
void
インスタンス
_requestData
オブジェクトのディープ クローンを返します。toObject
関数は次のようになります。() => {...}
-
戻り値
RequestData
-
-
toRequest
void
このインスタンスを Request に変換します。
toRequest
関数は次のようになります。() => {...}
-
戻り値
リクエスト
-
-
fromRequest
void
Request オブジェクトを、構造化クローン、または JSON 文字列化が可能なプレーン オブジェクトに変換します。
fromRequest
関数は次のようになります。(request: Request) => {...}
-
request
リクエスト
-
戻り値
Promise<StorableRequest>
-