workbox-background-sync

ウェブサーバーにデータを送信すると、リクエストが失敗することがあります。原因としては、ユーザーの接続が切断された場合や、サーバーがダウンしていることなどが考えられます。いずれの場合も、後でもう一度リクエストを送信してみることをおすすめします。

新しい 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'
);

BackgroundSyncPluginfetchDidFail プラグイン コールバックにフックし、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 のテストはやや直感的ではなく、さまざまな理由から困難です。

実装をテストする最善の方法は、以下を行うことです。

  1. ページを読み込み、Service Worker を登録します。
  2. パソコンのネットワークをオフにするか、ウェブサーバーをオフにします。
    • Chrome DevTools をオフラインで使用しないでください。DevTools のオフライン チェックボックスは、ページからのリクエストにのみ影響します。Service Worker のリクエストは引き続き通過します。
  3. Workbox バックグラウンド同期を使用してキューに追加するネットワーク リクエストを作成します。
    • リクエストがキューに追加されたかどうかは、Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests で確認できます。
  4. ネットワークまたはウェブサーバーをオンにします。
  5. 早期 sync イベントを強制するには、Chrome DevTools > Application > Service Workers に移動し、workbox-background-sync:<your queue name> のタグ名(<your queue name> は設定したキューの名前)を入力して [Sync] ボタンをクリックします。

    Chrome DevTools の同期ボタンの例

  6. 失敗したリクエストに対してネットワーク リクエストが処理され、リクエストが正常にリプレイされたため、IndexedDB データは空になっているはずです。

BackgroundSyncPlugin

fetchDidFail ライフサイクル コールバックを実装するクラス。これにより、失敗したリクエストをバックグラウンド同期キューに簡単に追加できます。

プロパティ

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

      リクエスト