金魚の糞アンチパターン(非同期処理の逐次化)
GWTではサーバとのやりとりを全ていわゆるAjax Requestで行います。従って非同期処理がコード中に多くは入り込むことになります。
- データをもらうために、2回サーバとやりとりを行うので2種類のAsycCallback実装クラスを作ります(LoadCountryクラス, LoadFruitクラス)。
- 2回のやりとりが終わった、というのを確実にするために、最初にデータAのリクエストを行い、その応答を処理した後デデータBのリクエストを行うようにします(LoadCountry#onSuccess)。そしてデータBの応答を処理した後、最終処理を実行します(LoadFruit#onSuccess)。
- 2.で実装した処理を開始します。すなわち、データAのリクエストを行います(47行目)。
- やりたい処理のかたまりと、実装上のかたまりが一致しない。
- 「Aの取得要求」と「取得したAの表示」は1まとまりにしたいが実装上分離してしまう。
- 「取得したAの表示」と「Bの取得要求」処理には本来関連が無いが1メソッド内にまとまってしまう。
- データAに関する処理とデータBに関する処理は本来前後関係や依存関係が無いにも関わらず、関係があるような表現になってしまう。
- データA,Bに関してはそれぞれ独立して処理してよいにも関わらず、データAの処理が終わった後でないとデータBの処理に進まない。
- データA,Bに関する処理はそれぞれ独立している。(必須ではないが)並行して処理しても構わない。
- ただし、起動完了処理はデータAに関する処理、データBに関する処理の双方が終了した後に実行されなければならない。
- エラーハンドリング。具体的にはAsyncCallback#onFailureの扱い。これはさほど難しいことでは無いと思います。
- Task間のデータ受け渡し型(ちなみに上の例ではTask間でデータのやりとりは発生していませんので説明には含まれていません)。今回の実装では、Barが後続Taskを開始する際に、前提TaskからもらったオブジェクトをList<?>型で渡すようになっています。このため型情報が消え、またリストに入っている順番もBarにTaskを登録した順になるので、受け取った方からすると扱いづらいです。
- サービス呼び出し(Ajax request)の最大同時実行数制御。Webブラウザ側でも抑制するようになっているそうなので、その機構に任せてしまっても良いように思われますが、どうなんでしょうね…
- フロー全体を管理している人がいないので、例えば進捗状況を確認したい、フローを外部から中断させたい、といった事を実現することがこのままでは不可能。
- StratifiedJS
- JSDeferred
- jQuery.Deferred
- CommonJSのPromises/A (これは実装ではなく仕様の名称?)
こういう形のコードのことをJavaScript界隈では"Pyramid of Doom"とか"Callback Pyramid of Doom"とか呼ぶそうですね。
« Ubuntu13.04(amd64)上でのAndroid開発環境セットアップメモ | トップページ | CentOS7でgitサーバ(gitプロトコル)を立てる »
この記事へのコメントは終了しました。
« Ubuntu13.04(amd64)上でのAndroid開発環境セットアップメモ | トップページ | CentOS7でgitサーバ(gitプロトコル)を立てる »
コメント