Fetchとは、高い耐障害性を求められるようなプロジェクトでは、XMLHttpRequestに(多分)取って代わるであろう、新しい機能です。
私のような古い人間にとって、コンピュータ関連でFetchといえば、RDBMSで1件ずつレコードを取ってくる処理のことを思い浮かべますが、こいつは全くの別物です。
リダイレクトの制御も行えるので、想定外のサーバにアクセスしてエラーとなるようなこともありあせん。
クロスオリジン(ドメイン)に於ける送受信の制御も容易に行えるので、細かなアクセス制御も行いやすくなっています。
FetchはInternet Explorerでは、たとえバージョン11でもサポートされていません。
そのため、全てのサンプルが動作しません。
別に、マイクロソフトの回し者ではありませんが、そろそろEdgeか他のWebブラウザを試してみませんか?
さて、このコードを見て、どれだけの人がすぐに理解できるのでしょう?
私は、最初???????????でした。『.then(...)』って何!
JavaScriptと言えば、HTMLと合わせて、決してプログラミングのプロでなくても、でもそれなりのHomeページ(流石にもう言わないか)を作ることができるツールでした。
しかし最近は、並列動作が実現されたり、バイナリデータが使えたり、ローカルファイルにも安全にアクセスできたりと、便利で高機能になった反面、プログラマにとっても少々厄介な代物になってきました(特に新しい機能についていくのが...)。
Fetch APIは、Promiseという非同期処理を扱うをオブジェクトにより実装されています。
『.then(...)』は、このPromiseオブジェクトのメソッドになります。
多分、Fetchの理解にはPromiseの理解が必須なのでしょうが、Promiseを完全に理解できるような解説は書けないので、ほんの触りです。
「Promiseなんて、分かってるぜ」という方は、読み飛ばしてください。というか、そんな方は「Fetchも分かってるぜ」な気もしますが...
MDNに解説「Promiseを使う」があるのですが、かなり難しいです。
それより簡単で解りやすい解説が書けるなら、その道のプロでやっていけるのでしょうが、残念ながらそうではないので、いくつかのサンプルでお茶を濁します。
なんとなく、イメージをつかめると良いのですが。
実行ボタンのHTMLです。
成功する条件では、delayを1000で初期化してサンプルの関数を呼び出し、失敗する条件では、delayを-1で初期化してサンプルの関数を呼び出します。
結果表示:
Promiseのコンストラクタに渡している関数では、delayが正しく初期化されていれば、7行目で非同期にresolveを呼び出し、初期化が不正であれば5行目でrejectを同期で呼び出します。
この関数の引数に指定したresolveとrejectが、それぞれpromise1のthenメソッドを実行する際の、第一引数と第二引数となります。
11行目の関数がresolve、13行目の関数がrejectになります。
しかし、これだけでは、全く良さが伝わりませんね。
便利な機能として、非同期処理の中での例外をキャッチして返してくれる、catchというメソッド用意されています。
下の例では、6行目のIDが間違っているので、例外が発生します。
結果表示:
いちいちコールバック関数の中で、例外発生時のエラー処理を書く必要が無くなります。少し、便利さが伝わりましたか。
Promiseのthenメソッドは、Promiseオブジェクトを返します。
返されたPromiseオブジェクトは、コールバック関数を、直前のPromiseオブジェクトが実行したコールバック関数の戻り値を引数として呼び出します。
結果表示:
2行目でPromiseのインスタンスpromise3を作成します。
このインスタンスは、引数のコールバック関数resolveを、500ミリ秒後に非同期で呼び出します(3行目)。
5行目でpromise3を実行します。この時渡しているコールバック関数は、「成功しました」という文字列を返すだけです。
この関数が、500ミリ秒後に非同期で実行されることになります。
promise3.thenは、新しいPromiseのインスタンスを返すので、これをpromise4に格納しています。
8行目でpromise4のthenメソッドを実行していますが、このメソッドに渡すコールバック関数の引数は、6行目で返した「成功しました」という文字列です。
受け取った引数の内容を、9行目で要素のテキストとして設定しています。
上記のサンプルは、Promiseのインスタンスを別々のフィールドに格納して実行していますが、以下のように書き換えることができます(多分Promiseを使う場合は、こちらの記述方法が本来かと)。
ちょっと、Fetchのサンプルに似てきましたね。
ということで、Fetchに戻ります。
最初に記載したものと同じサンプルです。
fetch関数はPromiseオブジェクトのインスタンスを返すので、成功時に呼び出すコールバック関数を引数にしてthenメソッドを呼び出します。
このコールバック関数が呼び出される時の引数は、Responseオブジェクトです。
サンプルでは、ロードしたデータを、blob()メソッドでBlobとして取得していますが、Responsオブジェクトには、他にもデータを返却するいくつかのメソッドが用意されています。
4行目は、Promiseチェーンでthenメソッドを呼び出します。
コールバック関数の引数は、3行目で返したBlobオブジェクトになります。
後は、受け取ったBlobからFileReaderでDataURLとしてロードして、指定のIMG要素に設定しているだけです。
せっかく新しい機能が出てきたのですから、古い機能との性能比較をしてみましょう。
テスト方法は、ローカルネットワーク内のサーバに、約1メガバイトのイメージを置き、それをスマホから100回ロードして時間を比較します。
表の先頭の数字はテストの回数です。5回テストしてその平均を出しています。
キャッシュが働かないように、リクエスト時のURLには、ミリ秒の現在日時をqueryとして付けています。
厳密なテストではないので、まあ目安程度といことで。
Firefox | Google Chrome | |||
---|---|---|---|---|
XMLHttpRequest | Fetch | XMLHttpRequest | Fetch | |
1 | 4622 | 4640 | 8334 | 8936 |
2 | 4823 | 4164 | 8253 | 8810 |
3 | 4813 | 4345 | 9263 | 8929 |
4 | 4969 | 4910 | 8362 | 8332 |
5 | 4583 | 4636 | 8381 | 9055 |
平均 | 4762 | 4539 | 8518.6 | 8812.4 |
単位はミリ秒です。まあ、なんとも微妙な結果ですが、驚きの結果でもあります。
XMLHttpRequestとFetchの違いは、誤差みたいなものですが、Firefoxの方が早いんですねえ、それも殆どダブルスコア。
もう一つ、驚きの結果を。
今度は、サーバ上の仮想環境で動作するWindow10から、同じテストを行った結果です。
Firefox | Google Chrome | Microsoft Edge | ||||
---|---|---|---|---|---|---|
XMLHttpRequest | Fetch | XMLHttpRequest | Fetch | XMLHttpRequest | Fetch | |
1 | 1527 | 1743 | 1757 | 2448 | 2025 | 776 |
2 | 1654 | 1690 | 2213 | 1683 | 2022 | 805 |
3 | 1297 | 1825 | 1591 | 1930 | 2055 | 1076 |
4 | 1508 | 1981 | 2741 | 1800 | 2471 | 1151 |
5 | 1427 | 1740 | 1413 | 1563 | 2024 | 1174 |
平均 | 1482.6 | 1795.8 | 1943 | 1884.8 | 2119.4 | 996.4 |
FirefoxとGoogle Chromeでは、スマホでの結果ほどではありませんが、やはりFirefoxの方が若干早いようです。
しかし、驚くのはMicrosoft EdgeのFetchでのパフォーマンスです。
XMLHttpRequestに比べて、半分以下の時間で処理が終了しています。
ネットワークトレースを見る限り、Keep-Aliveは効いているようですが、特に変わったところはありません。
今回のテスト方法などに関係なく、本当にMicrosoft EdgeのFetchがこれほど早いのだとすると、Chromiumベースになってしまうのはもったいない気がします。
逆に、Chromiumの(ということはGoogle Chromeも)Fetchが、Microsoft Edge程早くなったりすれば、Firefoxピーンチってことですが。
リダイレクトの制御や障害系に関しては、第二弾でやりたいと思います。
広告