Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
27
Help us understand the problem. What is going on with this article?
@nhiroki

Service Worker で横取りしたリクエストをネットワークにフォールバックさせたい時はただ return するべき

More than 5 years have passed since last update.

タイトルの通り、Service WorkerFetchEvent で横取りしたリクエストをネットワークにフォールバックさせたい場合は return するだけで OK です。

sw.js
self.AddEventListener("fetch", function(event) {
  return;  // network fallback happens
});

次のように明示的に fetch して respondWith() で返す必要はありません。むしろ fetch したレスポンスが一旦 Service Worker スクリプトに渡されるため、ただ return するよりも処理が遅くなります。

sw.js
self.AddEventListener("fetch", function(event) {
  event.respondWith(fetch(event.request));
});

以後、ただ return する場合を "return-fallback"、fetch して respondWith() で返す場合を "fetch-fallback" と表記します。

ベンチマーク

fetch-falback だと処理が遅くなることを確認するため、簡単なベンチマークを実施してみました。次のコードは 2048 bytes のファイルを 100 回リクエストします。リクエストは Service Worker によって横取りされますが、return-fallback もしくは fetch-fallback でネットワークにフォールバックされます。

sw.js
self.AddEventListener("fetch", function(event) {
  // return;
  //   OR
  // event.respondWith(fetch(event.request));
});
index.html
var start_time = 0;
navigator.serviceWorker.ready.then(function() {
      var promises = [];
      start_time = performance.now();
      for (var i = 0; i < 100; ++i)
        promises.push(fetch("2048-bytes.txt"));
      return Promise.all(promises);
    })
  .then(function() {
      console.log(performane.now() - start_time);
    });

これを Macbook Air (OS X 10.10, 1.7 GHz Intel Core i7, 8GB RAM) の Chrome 49.0.2568.0 で、それぞれ 5 回ずつ実行して計測してみました。サーバは localhost に立て、あらかじめ何回か実行して HTTP Cache や Service Worker をウォームアップしてから計測しています。フォールバックしたリクエストは HTTP Cache から返ります。結果は次の表の通りです。

return-fallback [ms] respondWith(fetch) [ms]
1 125.985 207.890
2 132.205 238.600
3 119.090 200.055
4 136.850 197.080
5 118.105 202.100
avg 126.447 209.145

return-fallback だと平均 126 ms、fetch-fallback だと平均 209 ms かかりました。このことから、リクエストをネットワークにフォールバックさせたい場合は respondWith(fetch(request)) で返すのではなく、ただ return した方が処理が早いことが分かります。

(おまけ) Chrome ブラウザの内部実装の話

「レスポンスが一旦 Service Worker スクリプトに渡される」とどうして遅くなるかというと、(1) ネットワーク処理を行うプロセス (Browser プロセス) と Service Worker のプロセスの間でプロセス間通信が発生するのと、(2) Service Worker のスクリプトにレスポンスが渡ると C++ と V8 の間で実行コンテキストが切り替わるからです。

current-data-flow.png

Chrome では event.respondWith(fetch(event.request)) というコードがあった場合には Service Worker プロセスをバイパスし、プロセス間通信やコンテキスト切り替えのオーバヘッドを取り除く最適化を入れるのはどうか、という提案が出されています (crbug)。しかし、return-fallback によってこれを回避でき、また最適化を入れるには複雑な変更が必要となるため、まだ実装されていません。

optimized-data-flow.png

謝辞

この記事の草稿を @kinu さんと @horo さんにチェックしてもらいました。ありがとうございます。


DISCLAIMER: ここで述べられている内容はすべて私の個人的な意見に基づくものであり、所属する組織、団体とは一切関係ありません。

27
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
27
Help us understand the problem. What is going on with this article?