DOMによるWeb Application Clientの可能性

Fetchとは、また懐かしい単語がWebに登場しましたねえ

Fetchとは、高い耐障害性を求められるようなプロジェクトでは、XMLHttpRequestに(多分)取って代わるであろう、新しい機能です。

私のような古い人間にとって、コンピュータ関連でFetchといえば、RDBMSで1件ずつレコードを取ってくる処理のことを思い浮かべますが、こいつは全くの別物です。

リダイレクトの制御も行えるので、想定外のサーバにアクセスしてエラーとなるようなこともありあせん。

クロスオリジン(ドメイン)に於ける送受信の制御も容易に行えるので、細かなアクセス制御も行いやすくなっています。

残念なお知らせ

FetchはInternet Explorerでは、たとえバージョン11でもサポートされていません。

そのため、全てのサンプルが動作しません。

別に、マイクロソフトの回し者ではありませんが、そろそろEdgeか他のWebブラウザを試してみませんか?

まずは簡単なサンプルから

Fetchの簡単なサンプル

さて、このコードを見て、どれだけの人がすぐに理解できるのでしょう?

私は、最初???????????でした。『.then(...)』って何!

JavaScriptと言えば、HTMLと合わせて、決してプログラミングのプロでなくても、でもそれなりのHomeページ(流石にもう言わないか)を作ることができるツールでした。

しかし最近は、並列動作が実現されたり、バイナリデータが使えたり、ローカルファイルにも安全にアクセスできたりと、便利で高機能になった反面、プログラマにとっても少々厄介な代物になってきました(特に新しい機能についていくのが...)。

Fetch APIは、Promiseという非同期処理を扱うをオブジェクトにより実装されています。

『.then(...)』は、このPromiseオブジェクトのメソッドになります。

Promise

多分、Fetchの理解にはPromiseの理解が必須なのでしょうが、Promiseを完全に理解できるような解説は書けないので、ほんの触りです。

「Promiseなんて、分かってるぜ」という方は、読み飛ばしてください。というか、そんな方は「Fetchも分かってるぜ」な気もしますが...

MDNに解説「Promiseを使う」があるのですが、かなり難しいです。

それより簡単で解りやすい解説が書けるなら、その道のプロでやっていけるのでしょうが、残念ながらそうではないので、いくつかのサンプルでお茶を濁します。

なんとなく、イメージをつかめると良いのですが。

Promiseの簡単なサンプル

Promiseの簡単なサンプル

実行ボタンのHTMLです。

成功する条件では、delayを1000で初期化してサンプルの関数を呼び出し、失敗する条件では、delayを-1で初期化してサンプルの関数を呼び出します。

実行ボタンのHTML

結果表示:

Promiseのコンストラクタに渡している関数では、delayが正しく初期化されていれば、7行目で非同期にresolveを呼び出し、初期化が不正であれば5行目でrejectを同期で呼び出します。

この関数の引数に指定したresolveとrejectが、それぞれpromise1のthenメソッドを実行する際の、第一引数と第二引数となります。

11行目の関数がresolve、13行目の関数がrejectになります。

しかし、これだけでは、全く良さが伝わりませんね。

例外の発生

便利な機能として、非同期処理の中での例外をキャッチして返してくれる、catchというメソッド用意されています。

下の例では、6行目のIDが間違っているので、例外が発生します。

例外発生のサンプル

結果表示:

いちいちコールバック関数の中で、例外発生時のエラー処理を書く必要が無くなります。少し、便利さが伝わりましたか。

Promiseチェーン

Promiseのthenメソッドは、Promiseオブジェクトを返します。

返されたPromiseオブジェクトは、コールバック関数を、直前のPromiseオブジェクトが実行したコールバック関数の戻り値を引数として呼び出します。

Promiseチェーンのサンプル

結果表示:

2行目でPromiseのインスタンスpromise3を作成します。

このインスタンスは、引数のコールバック関数resolveを、500ミリ秒後に非同期で呼び出します(3行目)。

5行目でpromise3を実行します。この時渡しているコールバック関数は、「成功しました」という文字列を返すだけです。

この関数が、500ミリ秒後に非同期で実行されることになります。

promise3.thenは、新しいPromiseのインスタンスを返すので、これをpromise4に格納しています。

8行目でpromise4のthenメソッドを実行していますが、このメソッドに渡すコールバック関数の引数は、6行目で返した「成功しました」という文字列です。

受け取った引数の内容を、9行目で要素のテキストとして設定しています。

上記のサンプルは、Promiseのインスタンスを別々のフィールドに格納して実行していますが、以下のように書き換えることができます(多分Promiseを使う場合は、こちらの記述方法が本来かと)。

Promiseチェーンのサンプル2

ちょっと、Fetchのサンプルに似てきましたね。

ということで、Fetchに戻ります。

簡単なサンプル再び

最初に記載したものと同じサンプルです。

Fetchの簡単なサンプル

fetch関数はPromiseオブジェクトのインスタンスを返すので、成功時に呼び出すコールバック関数を引数にしてthenメソッドを呼び出します。

このコールバック関数が呼び出される時の引数は、Responseオブジェクトです。

サンプルでは、ロードしたデータを、blob()メソッドでBlobとして取得していますが、Responsオブジェクトには、他にもデータを返却するいくつかのメソッドが用意されています。

4行目は、Promiseチェーンでthenメソッドを呼び出します。

コールバック関数の引数は、3行目で返したBlobオブジェクトになります。

後は、受け取ったBlobからFileReaderでDataURLとしてロードして、指定のIMG要素に設定しているだけです。

性能は?

せっかく新しい機能が出てきたのですから、古い機能との性能比較をしてみましょう。

テスト方法は、ローカルネットワーク内のサーバに、約1メガバイトのイメージを置き、それをスマホから100回ロードして時間を比較します。

表の先頭の数字はテストの回数です。5回テストしてその平均を出しています。

キャッシュが働かないように、リクエスト時のURLには、ミリ秒の現在日時をqueryとして付けています。

厳密なテストではないので、まあ目安程度といことで。

FirefoxGoogle Chrome
XMLHttpRequestFetchXMLHttpRequestFetch
14622464083348936
24823416482538810
34813434592638929
44969491083628332
54583463683819055
平均476245398518.68812.4

単位はミリ秒です。まあ、なんとも微妙な結果ですが、驚きの結果でもあります。

XMLHttpRequestとFetchの違いは、誤差みたいなものですが、Firefoxの方が早いんですねえ、それも殆どダブルスコア。

もう一つ、驚きの結果を。

今度は、サーバ上の仮想環境で動作するWindow10から、同じテストを行った結果です。

FirefoxGoogle ChromeMicrosoft Edge
XMLHttpRequestFetchXMLHttpRequestFetchXMLHttpRequestFetch
115271743175724482025 776
216541690221316832022 805
3129718251591193020551076
4150819812741180024711151
5142717401413156320241174
平均1482.61795.819431884.82119.4996.4

FirefoxとGoogle Chromeでは、スマホでの結果ほどではありませんが、やはりFirefoxの方が若干早いようです。

しかし、驚くのはMicrosoft EdgeのFetchでのパフォーマンスです。

XMLHttpRequestに比べて、半分以下の時間で処理が終了しています。

ネットワークトレースを見る限り、Keep-Aliveは効いているようですが、特に変わったところはありません。

今回のテスト方法などに関係なく、本当にMicrosoft EdgeのFetchがこれほど早いのだとすると、Chromiumベースになってしまうのはもったいない気がします。

逆に、Chromiumの(ということはGoogle Chromeも)Fetchが、Microsoft Edge程早くなったりすれば、Firefoxピーンチってことですが。

で、リダイレクトの制御は?

リダイレクトの制御や障害系に関しては、第二弾でやりたいと思います。

広告