@@ -568,7 +568,8 @@ DOM の構造を変更するトレーニング
568
568
DOM の構造を変更する必要性を考えてみます。
569
569
570
570
571
- 書籍検索サービスは、検索結果を下のように返したとしましょう。
571
+ 書籍検索サービスの API は、検索結果を
572
+ 下のように返したとしましょう。
572
573
573
574
``` javascript
574
575
[
@@ -816,6 +817,138 @@ button.addEventListener('click', function(event) {
816
817
サーバーと通信するトレーニング
817
818
818
819
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
+
819
952
#### 実習
820
953
821
954
下のテストが green になるように、
@@ -825,6 +958,14 @@ button.addEventListener('click', function(event) {
825
958
[ http://localhost:8000/stage5/ ] ( http://localhost:8000/stage5/ )
826
959
827
960
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
+
828
969
829
970
### ステージ6
830
971
@@ -853,3 +994,167 @@ button.addEventListener('click', function(event) {
853
994
修正してください。
854
995
855
996
[ 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