@@ -568,7 +568,8 @@ DOM の構造を変更するトレーニング
568568DOM の構造を変更する必要性を考えてみます。
569569
570570
571- 書籍検索サービスは、検索結果を下のように返したとしましょう。
571+ 書籍検索サービスの API は、検索結果を
572+ 下のように返したとしましょう。
572573
573574``` javascript
574575[
@@ -816,6 +817,138 @@ button.addEventListener('click', function(event) {
816817サーバーと通信するトレーニング
817818
818819
820+ #### サーバーとの通信
821+
822+ JavaScript にはサーバーと通信するための API が
823+ 用意されています。
824+
825+ - [ fetch API] ( http://www.hcn.zaq.ne.jp/___/WEB/Fetch-ja.html )
826+
827+ 現在策定中の新しい標準仕様
828+
829+ - [ XMLHttpRequest] ( https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest )
830+
831+ jQuery.ajax のようなショートハンドが使われる
832+ ことが多く、実際手で書くことはほとんどない
833+
834+
835+ 今回は、JavaScript の将来を見据えて、
836+ fetch API によるサーバーとの通信を
837+ トレーニングします。
838+
839+
840+ #### fetch API
841+
842+ fetch API は下のように書きます。
843+ このコードは、` /users.json ` を
844+ 取得します。
845+
846+ ``` javascript
847+ fetch (' /users.json' )
848+ .then (function (response ) {
849+ return response .json ()
850+ })
851+ .then (function (json ) {
852+ console .log (' parsed json' , json)
853+ })
854+ .catch (function (error ) {
855+ console .log (' parsing failed' , error)
856+ });
857+ ```
858+
859+ ` .then ` 、` .catch ` という不思議なメソッドで
860+ つながっています。
861+
862+
863+ #### Promise を使った非同期処理
864+
865+ さきほどの ` .then ` 、` .catch ` は、非同期処理の
866+ 結果を、引数に渡した関数で受け取るために
867+ 用意されています。
868+
869+ たとえば、` .then ` を使うと、正常にレスポンスが
870+ 受け取れた場合に関数を実行できます。
871+
872+ ``` javascript
873+ fetch (' /users.json' )
874+ .then (function (response ) {
875+
876+ // /users.json を正常に取得できたときに、
877+ // response をログに出力する
878+ console .log (response);
879+ });
880+ ```
881+
882+ エラーがあった場合は、ログ出力は実行されません。
883+
884+
885+ また、` .catch ` を使うと、エラーが発生した場合に
886+ 関数を実行できます。
887+
888+ ``` javascript
889+ fetch (' /users.json' )
890+ .catch (function (errror ) {
891+
892+ // /users.json の取得時にエラーがでたときに、
893+ // error をログに出力する
894+ console .log (error);
895+ });
896+ ```
897+
898+ こちらは、正常にレスポンスを受け取れた場合は、
899+ ログ出力は実行されません。
900+
901+ なお、先ほどの例のように ` .then ` と ` .catch ` を
902+ 同時につけることもできます。
903+
904+
905+ サーバーと通信するだけなのに、
906+ なんか複雑すぎるような…?
907+
908+
909+ 実はこの Promsie という複雑な仕組みを使う理由は、
910+
911+ - 並行非同期処理
912+ - 直列非同期処理
913+
914+ を書きやすくする、ということなのです。
915+
916+
917+ #### Promise による平行非同期処理
918+
919+ ` Promise.all ` を使います。
920+
921+ ``` javascript
922+ // 2つの Web API からレスポンスが欲しい!
923+
924+ Promise .all ([
925+ fetch (' /api/foo' ),
926+ fetch (' /api/bar' )
927+ ])
928+ .then (function (responses ) {
929+ var responseFoo = responses[0 ];
930+ var responseBar = responses[1 ];
931+ doSomething (responseFoo, responseBar);
932+ });
933+ ```
934+
935+
936+ #### Promise による直列非同期処理
937+
938+ ` .then ` で次々に処理を連結できます。
939+
940+ ``` javascript
941+ // Web API の結果を利用して別の API を実行したい!
942+
943+ fetch (' /api/foo' )
944+ .then (doSomething)
945+ .then (function () { return fetch (' /api/bar' ); })
946+ .then (doSomething)
947+ .then (function () { return fetch (' /api/buz' ); })
948+ .then (doSomething);
949+ ```
950+
951+
819952#### 実習
820953
821954下のテストが green になるように、
@@ -825,6 +958,14 @@ button.addEventListener('click', function(event) {
825958[ http://localhost:8000/stage5/ ] ( http://localhost:8000/stage5/ )
826959
827960
961+ #### 参考になる資料
962+
963+ - [ Promise に関する参考情報] ( https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise )
964+ - [ Promise 参考情報(重量級)] ( http://azu.github.io/promises-book/ )
965+ - [ fetch API に関する参考情報] ( https://github.com/github/fetch )
966+ - [ Github API に関する参考情報] ( https://developer.github.com/v3/search/ )
967+
968+
828969
829970### ステージ6
830971
@@ -853,3 +994,167 @@ button.addEventListener('click', function(event) {
853994修正してください。
854995
855996[ http://localhost:8000/stage6/ ] ( http://localhost:8000/stage6/ )
997+
998+
999+
1000+ 付録
1001+ ----
1002+
1003+
1004+ ### Promise について
1005+
1006+
1007+ #### Promise による平行非同期処理
1008+
1009+ Promise による平行非同期処理を通常のやりかたと、
1010+ Promise らしいやり方とでやってみました。
1011+
1012+ コードを比較してみてください。
1013+
1014+
1015+ ``` javascript
1016+ // 2つの Web API からレスポンスが欲しい!
1017+
1018+ var done = { foo: false , bar: false };
1019+ var responses = { foo: null , bar: false };
1020+ fetch (' /api/foo' ).then (function (responseFoo ) {
1021+ if (! done .bar ) {
1022+ done .foo = true ;
1023+ responses .foo = responseFoo;
1024+ return ;
1025+ }
1026+ doSomething (responseFoo, responses .bar );
1027+ });
1028+ fetch (' /api/bar' ).then (function (responseBar ) {
1029+ if (! done .foo ) {
1030+ done .bar = true ;
1031+ responses .bar = responseFoo;
1032+ return ;
1033+ }
1034+ doSomething (responses .foo , responseBar);
1035+ });
1036+ ```
1037+
1038+ レスポンス取得の待ち合わせ処理があり、
1039+ 状態を複数もつ厄介なコードにしあがっていますね。
1040+
1041+
1042+ ``` javascript
1043+ // 2つの Web API からレスポンスが欲しい!
1044+
1045+ Promise .all ([
1046+ fetch (' /api/foo' ),
1047+ fetch (' /api/bar' )
1048+ ])
1049+ .then (function (responses ) {
1050+ var responseFoo = responses[0 ];
1051+ var responseBar = responses[1 ];
1052+ doSomething (responseFoo, responseBar);
1053+ });
1054+ ```
1055+
1056+ ` Promise.all ` を使うと、待ち合わせ処理が
1057+ なくスッキリ!
1058+
1059+
1060+ #### Promise による直列非同期処理
1061+
1062+ 直列非同期処理についても、通常のやり方と、
1063+ Promise らしいやり方でやってみました。
1064+
1065+ コードを比較してみてください。
1066+
1067+
1068+ ``` javascript
1069+ // Web API の結果を利用して別の API を実行したい!
1070+
1071+ fetch (' /api/foo' ).then (function (responseFoo ) {
1072+ doSomething (responseFoo);
1073+ fetch (' /api/bar' ).then (function (responseBar ) {
1074+ doSomething (responseBar);
1075+ fetch (' /api/buz' ).then (function (responseBuz ) {
1076+ doSomething (responseBuz);
1077+ });
1078+ });
1079+ });
1080+ ```
1081+
1082+ コードがネストしているので、後ろの方の
1083+ 関数のスコープが深くなってしまっています。
1084+ 変数を追跡するのに手間がかかりそうです。
1085+
1086+
1087+ ネストの外に出すだけならば、終了コールバックを
1088+ 呼び出す[ 継続渡しスタイル] ( http://ja.wikipedia.org/wiki/%E7%B6%99%E7%B6%9A%E6%B8%A1%E3%81%97%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB ) で
1089+ 書くことができます。
1090+
1091+ ``` javascript
1092+ // Web API の結果を利用して別の API を実行したい!
1093+
1094+ fetch (' /api/foo' ).then (callbackFoo);
1095+
1096+ function callbackFoo (responseFoo ) {
1097+ doSomething (responseFoo);
1098+ fetchBar (callbackBar);
1099+ }
1100+
1101+ function fetchBar (callback ) {
1102+ fetch (' /api/bar' ).then (callback);
1103+ }
1104+
1105+ function callbackBar (responseBar ) {
1106+ doSomething (responseBar);
1107+ fetchBuz (callbackBuz);
1108+ }
1109+
1110+ function fetchBuz (callback ) {
1111+ fetch (' /api/buz' ).then (callback);
1112+ }
1113+
1114+ function callbackBuz (responseBuz ) {
1115+ doSomething (responseBuz);
1116+ }
1117+ ```
1118+
1119+ 流れが追いづらい!
1120+
1121+ クロージャー + 継続渡しスタイルを使うと…
1122+
1123+ ``` javascript
1124+ // Web API の結果を利用して別の API を実行したい!
1125+
1126+ fetch (' /api/foo' ).then (fetchBar (fetchBuz (doSomething)));
1127+
1128+ function fetchBar (callback ) {
1129+ return function (responseFoo ) {
1130+ doSomething (responseFoo);
1131+ fetchBar (callback);
1132+ };
1133+ }
1134+
1135+ function fetchBuz (callback ) {
1136+ return function (responseBar ) {
1137+ doSomething (responseBar);
1138+ fetchBuz (callback);
1139+ };
1140+ }
1141+ ```
1142+
1143+ これはこれで美しい…😌 ;
1144+
1145+ (JS に慣れるまではちょっと読みづらいと思います)
1146+
1147+
1148+ Promise らしいやり方をとると ` .then ` で
1149+ 次々に処理を連結できます。
1150+
1151+ ``` javascript
1152+ // Web API の結果を利用して別の API を実行したい!
1153+
1154+ fetch (' /api/foo' )
1155+ .then (doSomething)
1156+ .then (fetch (' /api/bar' ))
1157+ .then (doSomething)
1158+ .then (fetch (' /api/buz' ))
1159+ .then (doSomething);
1160+ ```
0 commit comments