DOMによるWeb Application Clientの可能性

非同期非同期非同期非同期!!!

JavaScriptが、だれでも簡単に動的なHTMLコンテンツを作成できる「ちょっとした道具」であった時代は遠い過去となり、今では本格的なアプリケーションを実現できるレベルの「強力な道具」にまで発展してきました。

ネットワークアクセスはもとより、ローカルファイルへのアクセスやあまつさえデータベースまで!

そして、それらは全て、非同期非同期非同期非同期非同期!!!!!!!

「いいかげんにしてくれ〜〜〜〜!!」

幸いというか、世界中に同じ様に感じた人たちが沢山いたと見えて、解決策が提示されています。

そのあたりをちょっと紹介します。

少々意地悪なサンプル

やりたいことはこうです。

XMLHttpRequestでリクエストし、その結果をJSONとして評価してfileNameプロパティよりファイル名を取得します。

そのファイル名に再度リクエストを行い、その結果をイメージのデータURLとして画面に表示します。

非同期呼び出しの嵐です。

背景色が変わっている箇所がイベントリスナの登録箇所ですが、あくまでもこれは正常ケースのみです。

本来であれば、ここにまだ"error"のイベントリスナ登録(場合によっては、"abort"のイベントリスナ登録も)と、HTTPステータスが200番代以外のエラーケースを追加する必要があります。

ちょっとうんざりですね。

まだ、処理が分割できればよいのですが、普通にコーディングするとネストしてしまうので、解りやすく分割するのは結構厄介です。

Promise参上

一つ前のFetchの章で登場したPromiseですが、そもそもこの様な非同期ネスト地獄(callback hell:コールバック地獄というらしい)をなんとかしようと考えられたものです。

なんとかしてもらいましょう。

Promiseチェーンに関しては前の章でも簡単に照会しましたが、コードが解りやすくも見やすくもならないので(個人の感想です)、ここでは非同期関数とawait演算子により実装します。

Promiseによる、レガシーのラッピング

XMLHttpRequestは、Promiseによる実装としてFetchが利用できますが、FileReaderは代替手段が存在しないので作成します。共通関数として汎用的に利用できるように実装します。

上記の関数は、FileReaderの処理をPromiseでラップした関数で、引数で渡されたblobDataURLに変換して返します(実際の返却値は、DataURLをラップしたPromiseになります)。

処理が成功した時にresolve(解決)、エラーが発生した時にreject(拒否)をそれぞれ呼び出しています。

resolveが呼び出されると、resolveに渡した引数の内容がPromiseでラップされて返却されます。

rejectが呼び出されると、rejectに渡した引数の値がスローされます。

非同期関数本体の実装

下記が、非同期関数の本体です。

関数宣言の前にasyncを付けることで、非同期関数となります。

非同期関数(async付きの関数)の中でawait演算子を付けてPromiseを実行すると、resolverejectのいずれかが呼び出されるまで、処理を停止します。

3行目fetchを使ってサーバにリクエストしています。awaitを付けているので、レスポンスが返るまで処理が停止します。

ネットワークエラーなどが発生した場合は、この呼び出しでエラーがスローされます。

レスポンスが返ると、4行目で結果を確認します。HTTPステータスが200番代で返るとresponse.oktrueとなるので、200番代以外のステータスが返った場合にエラーをスローします。

7行目でレスポンスからjsonオブジェクトを取得しています。

response.json()jsonオブジェクトをラップしたPromiseを返すので、awaitを付けて呼び出すことで、jsonオブジェクトそのものを取得します。

それ以降は、ほぼ同じ処理の繰り返しですが、14行目で先に定義した共通関数(readAsDataURL)を呼び出しています。

全体をtry〜catchで囲っていますが、このcatchで非同期処理の中で発生したエラーを含め、全てのエラーがキャッチできます。

先のXMLHttpRequestを使ったサンプルと同じことを実装している上に、エラー処理も取り込んでいる割には簡潔に記述できていると思うのですが、いかがでしょう。