Service Workerの利用について

この記事は編集レビューを必要としています。ぜひご協力ください

これは実験段階の機能です。
この機能は複数のブラウザーで開発中の状態にあります。互換性テーブルをチェックしてください。また、実験段階の機能の構文と挙動は、仕様変更に伴い各ブラウザーの将来のバージョンで変更になる可能性があることに注意してください。

この記事はService Workerを使い始めるための情報を提供するページです。基本的な設計、Service Workerの登録、新しいService Workerのインストールと有効化のプロセス、Service Workerの更新、キャッシュやレスポンスの操作を含めた、オフラインで動作するシンプルなアプリケーションの機能についてです。

Service Workersの前提条件

ウェブユーザーが長年苦しめられてきた主要な問題の一つは、接続を失うことです。この問題を解決するために様々な技術の開発が試みられ、私たちのオフラインページが示すように、いくつかの問題は解決済みです。しかし、アセットキャッシュとネットワークリクエストを全てコントロールする良いメカニズムはまだ存在しません。

以前の取組み - AppCache - は、キャッシュするアセットをとても簡単に指定することが可能になるため、良いアイデアに見えました。しかしながら、AppCacheには利用において数多くの取り決めがあり、アプリケーションが完全に取り決めに従わない場合は壊れてしまいました。さらに詳しいことはJake ArchibaldのApplication Cache is a Douchebagに書かれています。

Service Workersは最終的にこれらの問題を解決するでしょう。Service Workerの文法はAppCacheの文法と比べて複雑です。しかし、代わりに、あなたがAppCacheで動作させていたような振る舞いを素晴らしい粒度でコントロールするためにJavaScriptを使用することができます。あなたはこれまでの問題や、いろいろなことを処理できるようになります。Service Workerを使用することで、アプリケーションがはじめにキャッシュされたアセットを使用するよう簡単に設定することができます。そのため、一度ネットワークからデータを取得しておけば、オフラインでも初期の機能を提供できます(オフラインファーストとして一般的に知られています)。このようなオフラインの機能はネイティブアプリでは既に使用可能であり、ネイティブアプリがウェブアプリを差し置いて選ばれる理由の一つです。

Service Workersを始めるための設定

多くのService Workersの機能は、それらをサポートしているブラウザの最新版ではじめから利用できるようになっています。もしデモコードがあなたの環境で動作しない場合は、以下のいずれかの設定を行い、有効化してください。

  • Firefox Nightly: about:configの画面でdom.serviceWorkers.enabledをtrueに設定し、ブラウザを再起動してください。
  • Chrome Canary: chrome://flagsの画面でexperimental-web-platform-featuresを有効にし、ブラウザを再起動してください(現在いくつかの機能はChromeでははじめから有効になっています)。
  • Opera: opera://flagsの画面でSupport for ServiceWorkerを有効にし、ブラウザを再起動してください。

また、あなたのコードはHTTPSを通して提供される必要があります。Service Workersはセキュリティ上の理由からHTTPSを通して実行されるよう制限されています。そのため、GitHubは実験を行うのによい場所です。GitHubはHTTPSをサポートしています。

Note: Nightlyでまだ利用できない機能はService Worker用に特別にビルドされたFirefoxで確認できます。

基本設計

Service Workerでは、セットアップの際に下記のステップが一般的に観測されます。

  1. Service WorkerのURLがフェッチされ、serviceWorker.register()を通して登録されます。
  2. 成功した場合、Service WorkerはServiceWorkerGlobalScope;で実行されます。これは根本的に特殊なワーカーコンテクストで、メインスレッドから独立しています。DOMへのアクセスもありません。
  3. Service Workerはイベントの処理ができる状態です。
  4. Service Workerにコントロールされたページが今後アクセスされた時、ワーカーのインストールが試みられます。インストールのイベントは常に最初にService Workerへ送られます(このイベントはIndexedDBの設定やサイトアセットのキャッシュに使用することができます)。これはネイティブアプリやFirefox OSアプリのインストールと全く同じ種類 - 全てをオフラインで使用できるようにするための - の処理です。
  5. oninstallハンドラが完了すると、Service Workerはインストールされたと考えられます。
  6. 次のステップは有効化です。Service Workerがインストールされると、次に有効化イベントを受け取ります。onactivateの基本的な使用法は、以前のバージョンのService Workerスクリプトで使用したリソースのクリーンアップです。
  7. 現在Service Workerはページをコントロールしているでしょう。しかしregister()が成功した後に開かれたページのみです。例:ドキュメントはService Workerがあろうとなかろうと実行され、実行されている間はその状態を保ちます。つまり、ドキュメントが実際にコントロールされるには、リロードされる必要があるでしょう。

Promises

Promises は複数の非同期の動作を実行する優れたメカニズムです。Service Workerの働き方の中心となっています。

Promises は多くのすばらしいことを行うことができます。しかし今は知っておくべきことは、何かがpromiseを返したとき、末尾に.then()を繋げることによって、成功時や失敗時などのコールバックをその中に含めることができ、失敗時のコールバックを含めたいときは最後に.catch()を挿入することもできるということです。

昔ながらの同期的なコールバック構造と、それに相当する非同期的なpromiseを比較してみましょう。

sync

try {
  var value = myFunction();
  console.log(value);
} catch(err) {
  console.log(err);
}

async

myFunction().then(function(value) {
  console.log(value);
  }).catch(function(err) {
  console.log(err);
});

初めの例では、私たちはほかのコードが実行できるようになる前に、myFunction()が実行され、valueが返るのを待たなくてはなりません。2つ目の例では、myFunction()valuepromiseを返し、残りのコードも実行に移すことができます。promiseが解決されるとき、thenの中のコードが非同期的に実行されます。

それでは実例を - 動的に画像を読み込みたい、しかし表示しようとする以前の段階で読み込みが終了していることを確実にしたいときはどうするのでしょう。これは私たちがしたい標準的なことですが、少しの痛みとなることもあります。読み込まれた後に表示するためだけなら私たちは.onloadを使用することができます。しかし私たちがリッスンするより前に起こり始めたイベントはどうなるのでしょう。私たちは.completeを使うこともできたでしょう。しかしそれはまだfoolproofではありません。複数の画像のときはどうするのでしょう。そしてそれは同期的に行われるため、メインスレッドをブロックしてしまいます。

代わりに、私たちはこのようなケースを操作するための独自のpromiseを作成することができます(私たちのPromiseテストの例のソースコードを見てください。このコードが無事動いていることにも注目してください)。

註: 実際のService Workerの導入では非推奨のXMLHttpRequest APIよりもキャッシュとonfetchが使われるでしょう。Promiseの理解に集中できるようにそれらの機能はここでは使用されていません。

function imgLoad(url) {
  return new Promise(function(resolve, reject) {      
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'blob';

    request.onload = function() {
      if (request.status == 200) {
        resolve(request.response);
      } else {
        reject(Error('Image didn\'t load successfully; error code:' + request.statusText));
      }
    };

    request.onerror = function() {
      reject(Error('There was a network error.'));
    };

    request.send();
  });
};

私たちはresolverejectを引数に持つコールバック関数を引数にとるPromise()コンストラクタを使用して新しいpromiseを返します。この関数のどこかで、何が起きた時に成功とするか、また何が起きた時に失敗とするか - このケースでは200 OKのステータスが返るかどうか - を定義し、成功時にはresolveを、失敗時にはrejectを呼ぶ必要があります。関数内の残りの要素は、標準的なXHRのものですので、今は気にする必要はありません。

imgLoad()を呼ぶときは、私たちが期待している通り、読み込みたい画像へのurlを一緒に呼ばなくてはなりません。しかし残りのコードは、少し異なります。

var body = document.querySelector('body');
var myImage = new Image();

imgLoad('myLittleVader.jpg').then(function(response) {
  var imageURL = window.URL.createObjectURL(response);
  myImage.src = imageURL;
  body.appendChild(myImage);
}, function(Error) {
  console.log(Error);
});

関数呼び出しの末尾に、二つの関数を持つthen()メソッドをつなげています。一つ目の関数はpromiseが成功としてresolveされたときに実行されます。二つ目の関数はpromiseが失敗したときに呼び出されます。成功のケースでは、myImageの中で画像を表示して、bodyに付け加えています(この引数はpromiseの中のresolveメソッドに含まれるrequest.responseです)。失敗のケースではコンソールにエラーを返しています。

これらはすべて非同期的に起こります。

Note: promiseの呼び出しを互いにつなげていくこともできます。例えば:
myPromise().then(success, failure).then(success).catch(failure);

Note: Jake Archibaldの素晴らしいJavaScript Promises: there and back againを読むことでpromiseについてのより多くの発見をすることができます。

Service Workerのデモ

Service Workerの登録とインストールについてのとても基本的なデモンストレーションをするために、私たちはsw-testという、スターウォーズのレゴの画像を表示するシンプルなデモを作成しました。このデモでは、JSONオブジェクトから画像データを取得し、Ajaxを使用してページの下の行にある画像が表示される前に画像を読み込むために、Promiseを活用した関数を使用しています。私たちは今のところ物事を固定的でシンプルに保っています。デモはまたService Workerを登録し、インストールし、有効化します。そしてブラウザがサポートしているときは必要なすべてのファイルをキャッシュし、オフラインで動作するでしょう。



GitHubでソースコードを見ることができます。またサンプルの動きも見ることができます。私たちがここで強調したいことの一つはPromiseです(app.jsの17-42行目を見てください)。上のほうで見たPromiseのテストデモのコードを修正したものです。下記の部分が異なっています。

  1. オリジナルでは、ほしい画像を読み込むためのURLを渡しているだけですが、今回のバージョンでは、一つの画像の全ての情報を含むJSONを渡しています(JSONがどのような形かはimage-list.jsを見てください)。これはそれぞれのPromiseがresolveするすべてのデータが、非同期で動作するためには、Promiseとともに渡されなければならないためです。もしURLのみを渡し、for()ループの中で後からJSONに含まれるほかの要素にアクセスしようとすると、Promiseがfor()ループの進行と同時にresolveを返さないため、うまく動作しないでしょう(これは同期的な処理になります)。
  2. 配列とともにPromiseはresolveされます。resolve後の関数で読み込まれた画像のBlob、画像名、クレジットやaltテキストを利用可能にするためです(app.jsの26-29行目を見てください)。Promiseは一つの引数のみでresolveするので、もし複数の値とともにresolveしたい場合は、配列かオブジェクトを使用する必要があります。
  3. resolveされたPromiseの値にアクセスするためには、thenが期待する関数を使用します(app.jsの55-59行目を見てください)。初めは少し奇妙に見えるでしょうが、これがPromiseの動き方です。

Service workersに入る

service workerを取得してみよう!

workerを登録する

私たちのアプリのJavaScriptファイル— app.js —の最初のブロックは次の通りです。これは service workersを使用するエントリーポイントです。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) {
    // registration worked
    console.log('Registration succeeded. Scope is ' + reg.scope);
  }).catch(function(error) {
    // registration failed
    console.log('Registration failed with ' + error);
  });
};
  1. 外側のブロックは、登録を試みる前にservice workersがサポートされているか確認する機能検出試験を行います。
  2. 次に、アプリの内部にあるJavaScriptファイルをこのサイトのservice wokerに登録するために、ServiceWorkerContainer.register()関数を使用します(これはオリジンへのファイルの相対URLであって、JSファイルの参照ではないことに注意してください)。
  3. scopeパラメータはオプションで、service workerが制御するコンテンツのサブセットを指定するために使用できます。このケースでは、'/sw-test/' を指定しており、アプリのオリジン配下のすべてのコンテンツを意味しています。もしこれを指定しなくてもこの値が既定値になりますが、指定方法を示すために使用しています。
  4. promiseの構造に成功時の処理をチェーンするために、.then() promise関数を使用しています。promiseが成功裡に解決されたとき、内部のコードが実行されます。
  5. 最後にpromiseが拒否されたときに実行される.catch()関数をチェーンします。

これは、workerコンテクストで実行されるservice workerを登録しているため、DOMアクセスは含まれていません。続いて、通常ページの外側のservice workerでロードを制御するためにコードを実行します。

1つのservice workerで、複数のページを制御できます。スコープ内のページがロードされるたび、service workerはそのページにインストールされて動作します。そのため、service workerスクリプト内でのグローバル変数の扱いには注意が必要だということを心にとどめておいてください。各ページは独自のworkerを持ちません。

Note: プロキシサーバのように、service worker関数はリクエストとレスポンスをキャッシュなどからのアイテムに置き換えるように修正することができます。

Note: service workersのすらばらしいところのは、上述のような機能検出をした場合、service workersをサポートしないブラウザは通常予想される流儀通りにオンラインでアプリを使用できます。さらに、1つのページで AppCacheとSWを使用した場合、SWはサポートしていないがAppCacheはサポートしているブラウザはAppCacheを使い、両方サポートしているブラウザはAppCacheを無視してSWを採用します。

なぜservice workerの登録に失敗するのか?

次の可能性が考えられます:

  1. HTTPS通信を使用してアプリケーションを実行していない。
  2. service workerファイルへのパスが正しく書かれていない— アプリのルートディレクトリからではなく、オリジンからの相対パスが書かれている必要があります。例えば、workerが https://mdn.github.io/sw-test/sw.jsにあり、アプリのルートがhttps://mdn.github.io/sw-test/だとします。このとき、パスは/sw-test/sw.js だとします。このとき、パスは/sw-test/sw.jsと書かれるべきで/sw.jsではありません。
  3. service workerの指定先があなたのアプリと異なるオリジンである。これは許可されていません。

インストールとアクティベート:キャッシュを配置する

service workerを登録した後、ブラウザはページ/サイト上でservice workerをインストールおよびアクティベートしようとします。

インストールが成功裡に完了したとき、installイベントが発火されます。通常、installイベントはオフラインで実行する必要のある資産を、ブラウザのオフラインキャッシュ領域に配置するために使われます。これをするために、Service Workerの真新しいstorage API— cache — を使用します。cacheはservice worker上でグローバルに、レスポンスで提供された資産を保存したり、リクエストにキーしたりできます。 このAPIはブラウザの標準キャッシュと同じようにふるまいますが、ドメインに特化しています。キャッシュが不要であることを再度伝えるまで存在します。つまり、すべてを制御できます。

Note: Cache APIはすべてのブラウザでサポートされているわけではありません (詳細はBrowser support セクションを見てください)。今すぐこれを使いたいなら、Google's Topeka demoで利用されているポリフィルの使用か、IndexedDBに資産を格納することを検討してください。

コードサンプルを見ることからこのセクションを始めましょう。―これは私たちのservice workerで最初に見つかるブロックです

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});
  1. ここではservice worker(thisによって)にイベントリスナーを追加して、イベントにExtendableEvent.waitUntil() メソッドをチェーンしています―これはwaitUntil()の内部のコードが成功裡に実行されるまで、Service Workerがインストールされないことを保証します。
  2. waitUntil()内で、サイトリソースキャッシュのバージョン1であるv1と呼ばれる新しいキャッシュを生成するために、Caches.open()メソッドを使用します。これは生成されたキャッシュのためにpromiseを返します。ひとたび解決されたら、パラメータとしてキャッシュしたいすべてのリソースのオリジン相対URLの配列を受け取る、生成されたキャッシュのaddAll()を呼び出す関数をコールします。
  3. promiseが拒否された場合やインストールが失敗した場合、workerは何もしません。コードを修正して再度登録にトライすればOKです。
  4. インストールが成功した後、service workerがアクティベートします。最初にservice workerがインストール/アクティベートされたときには独特な使い方はありませんが、service workerを更新したときにより意義があります(後に続くUpdating your service worker を見てください)。

Note: localStorageはservice workerキャッシュと同じように動作しますが、同期処理のため、service workers内では許可されていません。

Note: あなたが望むなら、IndexedDBはservice workers内でデータ保存のためにのために使用できます。

リクエストにカスタムレスポンスする

サイトの資産はキャッシュされたので、service workersにキャッシュされたコンテンツを使って何かするように指示する必要があります。これはfetchイベントを使って簡単に実現できます。

イベントが発火するたび、指定されたスコープ内のドキュメントとこれらのドキュメントが参照するすべてのリソースを含む、service workerによって制御されたリソースの取得します(例えば、index.html が画像を埋め込むためにクロスオリジンリクエストすると、それはservice workerを通過します)。

イベントリスナーをservice workerにアタッチしてから、HTTPレスポンスをハイジャックしてマジックを使って更新するために、イベント上でrespondWith() メソッドを呼び出せます。

this.addEventListener('fetch', function(event) {
  event.respondWith(
    // magic goes here
  );
});

ネットワークリクエストしたURLとマッチするリソースを単純にレスポンスすることから始められます。

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request);
  );
});

使用可能なキャッシュと一致する場合、caches.match(event.request)がキャッシュで利用可能な同等のリソースをネットワークから要求された各リソースに一致するようにできます。マッチングは通常のHTTPリクエストと同様に、URLを経由して行われ、ヘッダを変更します。

マジックを定義するときの、いくつかのオプションを見てみよう(RequestオブジェクトとResponseオブジェクトの詳細については、Fetch API documentationを見て下さい)。

  1. Response() コンストラクタを使用し、任意のレスポンスを作成できます。この例では、単純なテキストデータが返却されます。

    new Response('Hello from your friendly neighbourhood service worker!');
    
  2. このより複雑な例では、一般的なHTTPレスポンスを再現するためにヘッダ情報をレスポンスに付与できることを示しています。この例では、レスポンスの種類のみをブラウザに通知しています。

    new Response('<p>Hello from your friendly neighbourhood service worker!</p>', {
      headers: { 'Content-Type': 'text/html' }
    })
    
  3. もしキャッシュにリソースがなかった場合は、リソースを取得する一般的なリクエストであるfetchで、使用可能なネットワークから新たにリソースを取得できます。

    fetch(event.request)
  4. もしキャッシュにリソースがなかったり、ネットワークが使用不可能だった場合に、match()を使用し、リクエストに対してフォールバック用のページを用意できます。

    caches.match('/fallback.html');
  5. FetchEventにより、返却されるRequestオブジェクトのプロパティから様々なリクエストの情報を取得できます。

    event.request.url
    event.request.method
    event.request.headers
    event.request.body
    

失敗したリクエストを復旧する

service worker cacheに一致する場合、caches.match(event.request) は優れていますが、マッチしない場合はどうでしょう?失敗時のハンドリングを何も提供しなければ、promiseはrejectして、 マッチしないときにはネットワークエラーが発生します。

幸運なことに、service workersのpromiseベースの構造は、成功に向けたさらなるオプションを設定するためにささやかなことができます。具体的には、以下のようにできます。

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).catch(function() {
      return fetch(event.request);
    })
  );
});

promiseがrejectsした場合、catch()関数はリソースに対して代わりに既定のネットワークリソースを返します。これはネットワークがサーバからリソースをロードして使用できることを意味します。

私たちが本当に賢いなら、リソースをネットワークから要求するだけではないはずです。後のリクエストがオフラインでもリソースを扱えるように、キャッシュにもセーブするでしょう。これは、余分な画像がStar Warsギャラリーに追加されたなら、アプリが自動的に画像をとらえてキャッシュすることを意味します。 以下の例はトリックを行っています。

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).catch(function() {
      return fetch(event.request).then(function(response) {
        return caches.open('v1').then(function(cache) {
          cache.put(event.request, response.clone());
          return response;
        });  
      });
    })
  );
});

ここでは、promiseを返すfetch(event.request)と共に、既定のネットワークリクエストを返しています。このpromiseが拒否されたとき、caches.open('v1')を使ってキャッシュをとらえる関数を実行して応答します。これもまた、promiseを返します。promiseが拒否されたとき、cache.put() がキャッシュにリソースを追加するために使われます。リソースはevent.requestから捕捉されてから、レスポンスはresponse.clone() でクローンされ、キャッシュに加えられます。クローンはキャッシュに挿入され、オリジナルのリクエストは呼び出したページに与えるためにブラウザに返されます。

これはなぜでしょう?なぜなら、リクエストとレスポンスのストリームは1度しか読み込めないからです。ブラウザにレスポンスを返しながらも、キャッシュするためには、クローンしなくてはならないのです。オリジナルはブラウザに返され、クローンはキャッシュに送られます。それらは1度ずつ読み込まれます。

これで私たちの抱えるトラブルは、リクエストがいずれのキャッシュともマッチせず、ネットワークが使用できない場合、いまだリクエストが失敗することだけになりました。何が起こってもユーザが少なくとも何かは取得できるように、既定のフォールバックを提供しましょう。

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).catch(function() {
      return fetch(event.request).then(function(response) {
        return caches.open('v1').then(function(cache) {
          cache.put(event.request, response.clone());
          return response;
        });  
      });
    }).catch(function() {
      return caches.match('/sw-test/gallery/myLittleVader.jpg');
    })
  );
});

失敗の可能性があるのは新しい画像の更新だけのため、 のフォールバック画像を選択しました。以前確認した、installイベントリスナでのインストールに依存している他の画像でも同じことができます。

更新されたコードパターン提案

2014年9月18日更新: 私たちのデモでは、qgustavorの提案されたリファクタリングをするために、コードブロックを再度更新しました。 少し合理的で理解しやすくなったはずです。

this.addEventListener('fetch', function(event) {
  var cachedResponse = caches.match(event.request).catch(function() {
    return fetch(event.request).then(function(response) {
      return caches.open('v1').then(function(cache) {
        cache.put(event.request, response.clone());
        return response;
      });  
    });
  }).catch(function() {
    return caches.match('/sw-test/gallery/myLittleVader.jpg');
  });
    
  event.respondWith(cachedResponse);
});

2015年3月25日更新: Ben Kellyの提案に従うために、コードブロックはさらに更新されました。 これはより標準的なpromiseチェーンを使用して、caches.open()が拒否されるのを待つことなく、ドキュメントにレスポンスを返しています。

var response;
var cachedResponse = caches.match(event.request).catch(function() {
  return fetch(event.request);
}).then(function(r) {
  response = r;
  caches.open('v1').then(function(cache) {
    cache.put(event.request, response);
  });  
  return response.clone();
}).catch(function() {
  return caches.match('/sw-test/gallery/myLittleVader.jpg');
});

service workerを更新する

service workerがすでにインストールされていたとしても、リフレッシュかページをロードすることで新バージョンのworkerを提供でき、バックグラウンドで新バージョンがインストールされますが、まだ有効ではありません。旧いservice workerを使ってロードされたページがもはやないときにだけ、有効になります。そのようなページがロードされなくなると、直ちに新しいservice workerが有効になります。

次のように、新しいservice worker内のinstallイベントリスナを更新してみましょう(新しいバージョン番号に注目してください)。

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v2').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        
             …

              // include other new resources for the new version...
      ]);
    });
  );
});

これが発生している間、以前のバージョンはまだfetchに対して応答します。新しいバージョンは、バックグラウンドでインストールされています。以前の v1キャッシュを妨げないように、新しいキャッシュをv2と呼んでいます。

どのページも現在のバージョンを使用していないとき、新しいworkerは有効化し、fetchに応答するようになります。

古いキャッシュを削除する

あなたはactivateイベントも取得できます。これは一般的に、実行中の以前のバージョンを破壊するために使われます。たとえば、古いキャッシュを取り除きます。これはディスクスペースがいっぱいになることを防ぐために、最早不要なデータを削除するのにも役立ちます — それぞれのブラウザはservice workerが使うために与えられたキャッシュストレージの容量に対して厳しい制限があります。ブラウザはディスクスペースを管理するために最善を尽くしていますが、オリジンのためのキャッシュストレージを削除するかもしれません。 一般的にブラウザはオリジンのためのデータをすべて削除するか、オリジンのためのデータを持っていません。

waitUntil()に渡されたPromiseは、完了するまで他のイベントをブロックするので、新しいキャッシュ上で初めてのfetchイベントを取得するときには、クリンナップ操作が完了していると確信できます。

this.addEventListener('activate', function(event) {
  var cacheWhitelist = ['v2'];

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      });
    })
  );
});

開発ツール

Chromeには、現在service workerのアクティビティやデバイス上のストレージを示すchrome://inspect/#service-workersがあります。また、より詳細な情報の表示やworker processを開始/停止/デバックできるchrome://serviceworker-internalsがあります。将来的には、不良または存在しない接続をエミュレートするスロットリング/オフラインモードを実装する予定です。これは本当に素晴らしい機能です。

Firefoxもservice workersに関する便利なツールを実装し始めています。

  • SWsで何が登録されているかやそれらが更新/削除されているかを見るために、about:serviceworkersにナビゲートできます。
  • テストを行う場合、Firefox開発ツールオプション内のオプション(ギアメニュー)で、"HTTPによるService Workerを有効(ツールボックスを開いたとき)"をチェックすることで、HTTPS 制約を回避できます。

仕様

Specification Status Comment
Service Workers Working Draft Initial definition.

ブラウザ実装状況

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support 40.0 44.0 (44.0)[1] No support 24 No support
install/activate events 40.0 44.0 (44.0)[1] No support (Yes) No support
fetch event/request/
respondWith()
40.0 44.0 (44.0)[1] No support No support No support
caches/cache

42.0

39.0 (39.0)[1] No support No support No support
Feature Android Chrome for Android Firefox Mobile (Gecko) Firefox OS IE Phone Opera Mobile Safari Mobile
Basic support   40.0 44.0 (44.0) (Yes) No support (Yes) No support
 install/activate events No support 40.0 44.0 (44.0) (Yes) No support (Yes) No support
fetch event/request/
respondWith()
No support 40.0 44.0 (44.0) (Yes) No support No support No support
caches/cache No support 40.0 39.0 (39.0) (Yes) No support No support No support

[1] Service workers (and Push) have been disabled in the Firefox 45 Extended Support Release (ESR.)

関連項目

ドキュメントのタグと貢献者

 このページの貢献者: kazu69, yhorie, asmsuechan, technohippy, kawakami, YuichiNukiyama, cosmology233
 最終更新者: kazu69,