JavaScript 初心者が JS の未来を見据えつつ、
基礎をひととおり身に付けるための資料です。
この README は npm run presentation
で
再生することができます。
このトレーニングの目標は、
モジュールを読み書きできるようになり、
自分の好きなモジュールを
つくれるようになることです。
トレーニングの前に、セットアップを
終わらせてしまいましょう。
この研修では Google Chrome を利用します。
上のリンクから Chrome をインストールしてください。
Node.js も利用しています。
こちらも上のリンクからインストールしてください。
JavaScriptTraining の学習履歴を残すために、
JavaScriptTraining リポジトリを fork します。
画面右上にある fork ボタンを押してください。
ここからはターミナル上での操作になります。
リポジトリの URL をコピーし、下のコマンドを
ターミナルで実行してください。
git clone コピーしたURL
下のコマンドをターミナルで実行してください。
cd JavaScriptTraining
npm install
下のコマンドをターミナルで実行してください。
なお、トレーニング中はこのコマンドを
終了しないでください。
npm run serve
ブラウザから http://localhost:8000 へ
アクセスしてください。
トレーニングの一覧が表示されていれば完了です。
JavaScript はウェブの言語です。
ウェブページにある特定の要素を
操作する手段として始まりましたが、
途方もなく成長しました。(JavaScript パターン, O'Reilly)
クラスというものがなく、
第一級オブジェクトである関数が
多くの仕事に使われます。(JavaScript パターン, O'Reilly)
Java や PHP が追加しはじめた
クロージャーや無名関数といった
機能は、JavaScript 開発者が
楽しんできた機能であり、
あるのが当然だと思われていました。(JavaScript パターン, O'Reilly)
['Foo', 'Bar', 'Buz'].forEach(function(name) {
console.log('hello ' + name);
});
(クロージャーはトレーニングで扱います)
- Web ブラウザ
- Node.js, io.js
JavaScript の特色のひとつは、ブラウザ上で
動作するということです。ブラウザ上で動作できる
言語はいくつかありますが、一般的に
JavaScript がよく使われます。
-
Web ページに独自の動きをつけたいとき
(一部は CSS でできます)
-
ページ遷移なしにサーバーと通信したいとき
-
パフォーマンスチューニング
-
フロントエンドエコシステム使いたいとき
(タスクランナーとかパッケージマネージャ)
- アニメーションの例: Si digital
- インタラクションの例: Vim.js
- Web API の例: Google Map
Chrome で http://localhost:8000 を開きます。
Web ページの適当な場所で
右クリック > 要素の検証
または
- Mac の人: ⌘ + ⌥ + i
- Windows の人:Ctrl + Shift + i
開発ツールを選択し、Esc キーを
数回おしてみてください。
すると、下に Console が出てきます。
この Console から、JavaScript を
実行することができます。
試しに alert('Foo');
と実行してみてください。
アラートポップアップが表示されます。
1 + 1
や 'Foo' + 'Bar'
なども実行できますね。
変数代入・参照もできますね。
var foo = 'bar';
foo;
ブラウザオブジェクトもいじってみましょう。
document.title = 'あなたとjava 今すぐダウンロード';
Chrome のタブ名が書き変わりました。
ページを再読み込みしてみてください。
これまでの変更はすべてリセットされます。
(つまり、この方法で本番リリースは
できないということですね!)
-
スクリプト読み込みパターン(推奨)
<script src="foo.js"></script>
-
インラインパターン
<script>alert('inline!');</script>
-
インラインイベントハンドラーパターン
<img src="buz.png" onerror="alert('onerror!')">
-
ブックマークレットパターン
<a href="javascript: alert('bookmarklet!')"></a>
(あるいはポエム)
JavaScript を効率的に書けるようにするために
最低限の開発環境を整えます。
JavaScript に精通していない人に
「どんなツールを使ったらいいですか?」と
聞かれることがよくあります。
私は「構文ハイライトと lint を使うといいですよ」、
と答えることにしています。
ひとつエピソードを紹介します。
JavaScript には静的型検査がないことや、
記号が多い構文や、まずい仕様がいくつもあるので、
ミスを犯しやすい言語の一つです。
たとえば、JavaScript には "use strict"
という
宣言があります。この宣言は JavaScript の
まずい仕様の一部をエラーにすることで修正を
促す効果があり、よく記述を推奨される宣言です。
あるプロジェクトで気を利かした
プログラマーが "use strict"
を
新しいスクリプトで使い始めました。
"use strict";
// Do something...
これは、開発時のミスを早期発見できるように
するよい方法です!
しかし、リリース後、新しいスクリプトとは
関係ないはずの JavaScript による
メニューが動作しなくなっていました。
なぜでしょうか?
これは、パフォーマンスチューニングの一環で
複数の JavaScript ファイルを結合していたこと、
ファイルに対する "use strict"
を利用していた
ことの2つが原因となって起きた不具合です。
"use strict"
が結合された
すべてのファイル全体に効くように
なり、"use strict"
に対応していない
古いファイルがエラーを出すように
なってしまっていたのでした。
このエピソードは、いわゆる初見殺しです。
「何それどうしてそんな仕様なの😱」
と言いたくなりますね。
悲しいことに、このような落とし穴は
JavaScript の仕様・文化に数多く
潜んでいます。
これらのミスをどのようにしたら
防げるのでしょうか?
ESLint や JSHint、 JSLint のような lint を
使いましょう。
たとえば、ESLint を実行すると
下のような警告が出されます。これによって、
潜在的な不具合をだいぶ減らせるようになるのです。
Use the function form of "use strict".
(関数形式の "use strict" を使ってね)
ミスを防ぐためには、「頑張る」とか
「注意する」のような精神論ではなく、
lint によって防止することが必要です。
あなたが JavaScript にまだ精通していないので
あれば、真っ先にいれるべきは lint なのです。
-
lint をかけてみる
ターミナルで下のコマンドを実行してください。 ESLint が実行されます。
gulp lint-stage1
-
構文ハイライトを効かせる
ほとんどのエディタは構文ハイライトを
サポートしています。Vim のように
構文ハイライトが選べるのであれば、
よりミスのわかりやすい構文ハイライトを
利用します。
DOM 要素を取得するトレーニング
Web ページは HTML のタグによって
構成されています。
この 3D ビューを見ると、複数の HTML タグから
Web ページが構成されていることがよくわかります。
この HTML タグは下のような木構造をとっています。
実際に、mixi-inc/JavaScriptTraining を開き、
開発コンソールのElement タブに表示されている
構造を確認してみてください。
DOM (Document Object Model) は、この HTML のタグを
JavaScript の世界で操作することができる API です。
HTML タグは、JavaScript の世界で
DOM 要素というオブジェクトとして扱われます。
HTML のタグを JavaScript 側で操作するためには
HTML のタグを HTML 文書から取り出し、
JavaScript の世界へと取ってこなければなりません。
このステージでは、HTML から DOM 要素を
取得するという操作について学びます。
HTML タグには、目印となるいくつかの情報が
付属しています。たとえば、下の div タグには
ID 属性が付属しています。
<div id="foo">foo</div>
JavaScript はこの目印を DOM API に渡すことで、
DOM 要素を取得することができます。
var div = document.getElementById('foo');
他にも CSS クラスやタグ名、その他の属性から、
DOM 要素を取得することができます。
<div class="foo">foo</div>
また、目印の指定の仕方のひとつに、
CSS セレクタがあります。
たとえば、foo
という ID のついたタグであれば、
<div id="foo">foo</div>
var div = document.querySelector('#foo');
というように、ID の先頭に #
をつけた
IDセレクタ #foo
で、取得したい DOM 要素を指示します。
この方法の利点は、複雑な位置にある DOM 要素を
取得することができるということでしょう。
下の HTML の bar を包む div
タグにあたる
DOM 要素を取得する場合、
<div class="foo">
<div>foo</div>
<div>bar</div>
</div>
var div = document.querySelector('.foo div:last-child');
というように、CSS クラスセレクタ .foo
に
該当する要素の子要素を子孫セレクタ (スペース) で
取得し、このうち div
タグから CSS 疑似セレクタ
:last-child
にマッチするものを取得する
という操作になります。
図にすると、このようになります。
CSS セレクタについては、MDN のCSS リファレンス が
参考になります。
仕様を見ないと我慢ならぬ!という立派な方は、
セレクタ Level 3 仕様 を見るとよいでしょう。
下のテストが green になるように、
public/stage1/tests.js
を
修正してください。
Lint をかけてみましょう。
gulp lint-stage1
警告があれば、修正してみてください。
DOM 要素の属性・テキストを変更する
トレーニング
このステージでは、スタイルの変更や
表示文字列を変更するやり方を学びます。
DOM 要素には、
- 先ほど DOM API に渡した ID 属性
- CSS クラス属性などの目印となる属性
- 見た目を操作するスタイル属性
などが付属しています。
(DOM の属性の一覧は MDN DOM リファレンス を
参照してください)
このうち、スタイル属性を編集すると、DOM 要素の
見た目を変化させることができます。
たとえば、Github のヘッダの octocat の
style.color 属性を変更してみました。
このスタイル属性は CSS の仕様と対応するように
定められています。
ただ、アニメーションを含め、見た目の変更は
CSS クラス名の追加/編集/削除によっておこなう
ことが推奨されてきています。
JavaScript は見た目の変更のきっかけを
与えるだけにとどめるのが、上手な
HTML/JavaScript/CSS 分業の基本です。
残念なお知らせですが、
今回のトレーニングは CSS を
書けるようになることが目的ではないので、
レガシーな element.style を編集するやり方を
学びます。
レガシー… 😱
まあ、肩を落とさないでください。
レガシーとはいえ、レガシーブラウザーを
相手にするライブラリを読み書きするときには、
どうしても必要になります。
ちなみに、「レガシー?帰らせていただきます」は
フロントエンドエンジニアとして大切な感覚ですので
大事にしてください。
下のテストが green になるように、
public/stage2/tests.js
を
修正してください。
Lint をかけてみましょう。
gulp lint-stage2
警告があれば、修正してみてください。
DOM の構造を変更するトレーニング
このステージでは、DOM の属性ではなく、
構造を変更するトレーニングをおこないます。
たとえば、書籍を検索する Web API を使って、
書籍検索サービスを開発することを例に、
DOM の構造を変更する必要性を考えてみます。
書籍検索サービスの API は、検索結果を
下のように返したとしましょう。
[
{
"title": "紅",
"score": 5
},
{
"title": "円環少女",
"score": 5
},
{
"title": "SHI-NO -シノ- 黒き魂の少女",
"score": 5
},
...
]
このとき、画面に表示するために下のような HTML を
追加することになります。
<ul>
<li>
<div>紅</div>
<div>★★★★★</div>
</li>
<li>
<div>円環少女</div>
<div>★★★★★</div>
</li>
<li>
<div>SHI-NO -シノ- 黒き魂の少女</div>
<div>★★★★★</div>
</li>
...
</ul>
サーバー側で上のような HTML を
生成してもよいのですが、
JavaScript による DOM 構造の操作に
よって実現させることができれば、
軽快な書籍検索サービスをつくることができます。
(ページのリロードが必要ないからですね!)
このように、Web サービスの使いやすさを
追求するためには、JavaScript による
DOM 構造の操作が重要なのです。
下のテストが green になるように、
public/stage3/tests.js
を
修正してください。
Lint をかけてみましょう。
gulp lint-stage3
警告があれば、修正してみてください。
DOM イベントを利用するトレーニング
ユーザーがボタンを押したり、
検索欄に入力したとき、JavaScript は
DOM イベントをうけとることができます。
たとえば、下のボタンは click イベントを
引きがねとして、DOM 構造を書き換えます。
click イベント以外にも多様なイベントがあります:
- dblclick: 要素上でダブルクリックされたとき
- mousemove: 要素上でポインタが移動しているとき
- scroll: 画面がスクロールされたとき
- unload: 次のページに遷移する直前
- keydown: キーボードが押されたとき
- animationstart: CSS アニメーションが開始したとき
- offline: ブラウザがオフラインになったとき
ここに載っていないイベントの一覧は
MDN DOM イベントリファレンス を
参照するとよいでしょう。
試しに、このページを制御する JavaScript が
監視しているイベントを見てみましょう。
開発ツールを開き、Elements タブの中段にある
Event Listeners タブを開いてみてください。
たくさんのイベントが登録されていますね。
では、これらのイベントの使い方を解説していきます。
DOM のイベントを JavaScript 側で監視するには、
いくつかの方法があります。
-
addEventListener スタイル
var button = document.getElementById('button'); button.addEventListener('click', function(event) { console.log(event); });
-
インラインイベントハンドラースタイル
<button onclick="console.log(this)"></button>
-
イベント属性スタイル(レガシー)
var button = document.getElementById('button'); button.onclick = function(event) { console.log(event); };
このうち、 addEventListener
スタイルが
お行儀のよい方法だといわれ、
推奨されてきました。
// お行儀のよさ
button.addEventListener('click', function(event) {
console.log(event);
});
しかし、AngularJS という最近の
フレームワークでイベント属性スタイルを
積極的に採用する動きもあります。
<!-- AngularJS 1.x -->
<button ng-click="changeName(newname.value)"> Change Name </button>
<!-- AngularJS 2.x -->
<button (click)="changeName(newname.value)"> Change Name </button>
このような場合は、無理に addEventListener
を
使わずに、そのフレームワークのしきたりに
従うとよいでしょう。
次に、DOM のイベント伝搬(propagation)
について解説します。
下のようなボタンを例に、伝搬の必要性を
考えていきます。このボタンは、アイコンつきで
表示されることを意図しています。
<button><img src="icon.png">Click me</button>
この button 要素の click
イベントを
監視することを考えます。
button 要素に addEventListener
すればよいように
見えますが、アイコン画像をクリックされた場合
どうなるのでしょうか?
実は、子要素で発生した DOM イベントは
親要素からも監視することができます。
この仕組みが DOM イベントの伝搬です。
addEventListener
の引数で
1-2-4 か 2-3-4 のどちらかを選べます。
-
capturing フェーズ
ルート要素からイベントの対象要素まで降りていく
-
target フェーズ
イベントの対象要素に到着
-
bubbling フェーズ
イベントの対象要素からルート要素まで昇っていく
-
ブラウザ既定の処理がおこなわれるフェーズ
リンクなどによる画面遷移がおこなわれる
先ほどのボタンの例では、img 要素の
click イベントは bubbling によって
button 要素まで通知されるのです。
<button><img src="icon.png">Click me</button>
// button への addEventListener で OK
button.addEventListener('click', function(event) {
// Do something
});
下のテストが green になるように、
public/stage4/tests.js
を
修正してください。
Lint をかけてみましょう。
gulp lint-stage4
警告があれば、修正してみてください。
非同期処理のトレーニング
JavaScript の美しい機能のひとつに
非同期処理があります。
下のコードは 1 秒まったあとに
console.log(1)
を実行するコードです。
setTimeout(function() {
console.log(1);
}, 0);
console.log(2);
このコードを実行すると、1
と 2
の
どちらか先に表示されるでしょうか。
答えは、2
です。setTimeout
に登録された
関数はいま実行途中のすべての関数が終了してから
呼び出されます(JavaScriptのsetTimeoutを理解する)。
ここでは、関数を実行するタイミングを
後回しにすることによって、待っている間に
別のことができる、ということを覚えてください。
この待ち時間の間に別の処理を実行する
やり方を非同期処理と呼びます。
非同期処理の代表例といえばサーバーとの通信です。
サーバーとの通信はネットワークを通過するため、
かなりの時間がかかります。そこで、レスポンスが
返ってくるまでの間に、別の処理をおこなうことに
よって、時間を有効活用することが重要になります。
JavaScript にはサーバーと非同期に通信するための
API が用意されています。
-
現在策定中の新しい標準仕様
-
jQuery.ajax のようなショートハンドが使われる
ことが多く、実際手で書くことはほとんどない
今回は、JavaScript の将来を見据えて、
Fetch API によるサーバーとの通信を
トレーニングします。
Fetch API は下のように書きます。
このコードは、/users.json
を
取得します。
fetch('/users.json')
.then(function(response) {
return response.json()
})
.then(function(json) {
console.log('parsed json', json)
})
.catch(function(error) {
console.error('parsing failed', error)
});
.then
、.catch
という不思議なメソッドで
つながっています。
さきほどの .then
、.catch
は、非同期処理の
結果を、引数に渡した関数で受け取るために
用意されています。
たとえば、.then
を使うと、正常にレスポンスが
受け取れた場合に関数を実行できます。
fetch('/users.json')
.then(function(response) {
// /users.json を正常に取得できたときに、
// response をログに出力する
console.log(response);
});
エラーがあった場合は、ログ出力は実行されません。
また、.catch
を使うと、エラーが発生した場合に
関数を実行できます。
fetch('/users.json')
.catch(function(errror) {
// /users.json の取得時にエラーがでたときに、
// error をログに出力する
console.error(error);
});
こちらは、正常にレスポンスを受け取れた場合は、 ログ出力は実行されません。
なお、先ほどの例のように .then
と .catch
を
同時につけることもできます。
サーバーと通信するだけなのに、
なんか複雑すぎるような…?
実はこの Promise という複雑な仕組みを使う理由は、
- 並行非同期処理
- 直列非同期処理
を書きやすくする、ということなのです。
Promise.all
を使います。
// 2つの Web API からレスポンスが欲しい!
Promise.all([
fetch('/api/foo'),
fetch('/api/bar')
])
.then(function(responses) {
var responseFoo = responses[0];
var responseBar = responses[1];
doSomething(responseFoo, responseBar);
});
.then
で次々に処理を連結できます。
// Web API の結果を利用して別の API を実行したい!
fetch('/api/foo')
.then(doSomething)
.then(function() { return fetch('/api/bar'); })
.then(doSomething)
.then(function() { return fetch('/api/buz'); })
.then(doSomething);
下のテストが green になるように、
public/stage5/tests.js
を
修正してください。
Lint をかけてみましょう。
gulp lint-stage5
警告があれば、修正してみてください。
モジュールを実装するトレーニング
JavaScript は言語機能としてモジュールの
仕組みをもっていません。
言語機能としてのモジュールシステムを利用するには
ECMAScript 6 を待たなければなりません。
とはいっても、みんなモジュールを使いたかったので、
さまざまなモジュールシステムとそれに付随する
エコシステムが開発されてきました。
- bower
- component
- jam
- volo
- npm with browserify
- spm
- jspm
- duo
(source: wilmoore/frontend-packagers)
あ…めっちゃ多い…😵
今回は、利用方法がシンプルな「bower」を使います。
bower は、JavaScript、HTML、CSSなどを
共有して使えるようにするフロントエンドの
エコシステムです。
他の人が作ったモジュールを利用することや、
自分が作ったモジュールを公開することも
できます。
ただ、bower はモジュール読み込みの
仕組みを提供していません。
この部分は RequireJS など、別の
モジュールシステムに頼ることになります。
どのモジュールシステムに対応するかという選択は、
bower によって読み込まれるパッケージ側に
裁量(責務)があります。
この方針を公式ドキュメントは端的に
言い表しています。
How you use packages is up to you.
(どのようにしてパッケージを使うのかはあなた次第です)
まず、bower を実行することを体験してみます。
bower の設定ファイル bower.json を対話的に
作成します。
cd public/stage6/sample
bower init
あとは説明に従って選択していくと、bower の
パッケージ設定ファイル bower.json
が作成されます。
このパッケージの名前を指定します。
パッケージとして公開する場合には、既に同じ
パッケージ名が存在していないか確かめる必要が
あります。
この研修では、公開/非公開を問わないので、
お好きな名前をつけてください。
このパッケージのバージョンを指定します。
バージョンの形式は Semantic Versioning に
準拠しています。
この形式は、一般的に X.Y.Z
と記述されます。
X
は major version(後方互換性がなくなる変更)Y
は minor version(前方互換性がなくなる変更)Z
は patch version(バグ修正など)
今回は開発版なので、0.0.0 にしておきましょう
(major versionの 0 は開発版であることを示します)。
パッケージの簡単な概要を記述します。
有名どころの説明をみてみます。
- bootstrap: The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.
- angular-latest: HTML enhanced for web apps
- less.js: Leaner CSS
このパッケージが外部のパッケージに公開したい
ファイルを指定します。文字列と配列が指定できます。
今回は空で問題ありません。
このパッケージが外部にエンドポイントを公開する
方法を明示します。
- amd: Asynchronouse Module Definition (参考資料)
- es6: EcmaScript 6 (参考資料)
- globals: グローバル変数経由でエンドポイント公開
- node: Node.js
- yui: YUI (メンテ停止したのでもうやめましょう)
今回は何も選択しないで問題ありません。
このパッケージを検索でヒットさせるための
キーワードを指定します。
このパッケージの作者を指定します。
好きなライセンスを選ぶとよいです。
デフォルトは MIT ライセンスです。
このパッケージの情報が見られる URL を記述します。
既に bower_components
に含まれている
コンポーネントをパッケージ設定に
含まれるようにするかどうかを指定します。
n で構いません。
.gitignore
などのファイルから、
パッケージに含めないファイルの指定を
読み込むかどうか指定します。
y で読み込ませます。
12. would you like to mark this package as private which prevents it from being accidentaly published to the registry?
bower のレジストリへ登録できないようにするか
どうか指定します。
y でレジストリへの公開ができないように設定します。
この設定で問題なければ y を入力します。
いよいよ、パッケージを追加していきます。
パッケージは Search Bower packages で
検索することができます。
もしくは
bower search Buttons
でも検索することができます。
では、試しに Buttons パッケージを
追加してみましょう。
下のコマンドによって、Buttons パッケージが、
bower_components
以下に配置されます。
bower install --save Buttons
--save
はパッケージ設定に依存ファイルを
追記する効果があります(bower.json
の
内容が変化しているので、見てみてください)。
ここで設定に追記されたパッケージは、
次回から bower install
でまとめて
取得することができるようになります。
今回は、簡単のために script タグで直接
bower_components
以下の JavaScript/CSS を
読み込みます。
今回の実習はテスト駆動形式ではありません。
満足のいく Web アプリケーションが書けたら、
qualityOfYourAppliation
に true
を
代入してください。
Lint をかけてみましょう。
gulp lint-stage6
警告があれば、修正してみてください。
よくあるイディオムを読むトレーニング
このステージでは、よくある JavaScript の
不思議な書き方を学びます。
なお、今回はヒントがありません!
ぜひ自分で調べて、結果を確かめてみてください!
なお、興味のある方は、ステージ「闇」に
挑戦してみてくださいね!
.skip
を削除すれば挑戦できるようにに
なります。
下のテストが green になるように、
public/stage7/tests.js
を
修正してください。
Lint をかけてみましょう。
gulp lint-stage7
警告があれば、修正してみてください。
解答は 2015-example-solution で見られます!
Promise による並行非同期処理を通常のやりかたと、 Promise らしいやり方とでやってみました。
コードを比較してみてください。
// 2つの Web API からレスポンスが欲しい!
var done = { foo: false, bar: false };
var responses = { foo: null, bar: false };
fetch('/api/foo').then(function(responseFoo) {
if (!done.bar) {
done.foo = true;
responses.foo = responseFoo;
return;
}
doSomething(responseFoo, responses.bar);
});
fetch('/api/bar').then(function(responseBar) {
if (!done.foo) {
done.bar = true;
responses.bar = responseFoo;
return;
}
doSomething(responses.foo, responseBar);
});
レスポンス取得の待ち合わせ処理があり、
状態を複数もつ厄介なコードにしあがっていますね。
// 2つの Web API からレスポンスが欲しい!
Promise.all([
fetch('/api/foo'),
fetch('/api/bar')
])
.then(function(responses) {
var responseFoo = responses[0];
var responseBar = responses[1];
doSomething(responseFoo, responseBar);
});
Promise.all
を使うと、待ち合わせ処理が
なくスッキリ!
直列非同期処理についても、通常のやり方と、
Promise らしいやり方でやってみました。
コードを比較してみてください。
// Web API の結果を利用して別の API を実行したい!
fetch('/api/foo').then(function(responseFoo) {
doSomething(responseFoo);
fetch('/api/bar').then(function(responseBar) {
doSomething(responseBar);
fetch('/api/buz').then(function(responseBuz) {
doSomething(responseBuz);
});
});
});
コードがネストしているので、後ろの方の
関数のスコープが深くなってしまっています。
変数を追跡するのに手間がかかりそうです。
ネストの外に出すだけならば、終了コールバックを
呼び出す継続渡しスタイル で
書くことができます。
// Web API の結果を利用して別の API を実行したい!
fetch('/api/foo').then(callbackFoo);
function callbackFoo(responseFoo) {
doSomething(responseFoo);
fetchBar(callbackBar);
}
function fetchBar(callback) {
fetch('/api/bar').then(callback);
}
function callbackBar(responseBar) {
doSomething(responseBar);
fetchBuz(callbackBuz);
}
function fetchBuz(callback) {
fetch('/api/buz').then(callback);
}
function callbackBuz(responseBuz) {
doSomething(responseBuz);
}
流れが追いづらい!
クロージャー + 継続渡しスタイルを使うと…
// Web API の結果を利用して別の API を実行したい!
fetch('/api/foo').then(fetchBar(fetchBuz(doSomething)));
function fetchBar(callback) {
return function(responseFoo) {
doSomething(responseFoo);
fetch('/api/bar').then(callback);
};
}
function fetchBuz(callback) {
return function(responseBar) {
doSomething(responseBar);
fetch('/api/buz').then(callback);
};
}
これはこれで美しい…😌
(JS に慣れるまではちょっと読みづらいと思います)
Promise らしいやり方をとると .then
で
次々に処理を連結できます。
// Web API の結果を利用して別の API を実行したい!
fetch('/api/foo')
.then(doSomething)
.then(function() { return fetch('/api/bar'); })
.then(doSomething)
.then(function() { return fetch('/api/buz'); })
.then(doSomething);