Skip to content
This repository was archived by the owner on Jun 20, 2023. It is now read-only.

Commit 6574992

Browse files
author
Kuniwak
committed
Improve stage5
1 parent 816d759 commit 6574992

File tree

4 files changed

+1074
-18
lines changed

4 files changed

+1074
-18
lines changed

README.md

Lines changed: 306 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ DOM の構造を変更するトレーニング
568568
DOM の構造を変更する必要性を考えてみます。
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

Comments
 (0)