本記事は一人アドベントカレンダー企画の一つです。
30代未経験エンジニアが25日後にJavaScriptをマスターするカレンダー
JAVASCRIPT.INFOを元にJavaScriptを勉強していき、そこで学んだ知識をアウトプットしていきます。
25日でJAVASCRIPT.INFOをやりきり、未経験エンジニアがJavaScriptをマスターする過程を投稿していきます。
11.1 前置き: コールバック
知らない単語
- 非同期処理とは
- ある処理が終了するのを待たずに、別の処理を実行する事
学んだこと
- 非同期処理
上記にもある通り、非同期処理は処理が終了するのを待たずに、別の処理を実行するので前の処理が追い抜かしてしまう可能性がある
同期処理は、逆に最初のコードから次のコードへと順次処理されていく事である
同期処理だと、時間がかかる処理が終了するまで次の処理を行えないと効率が悪いので、非同期処理が対策として存在する
多くの関数は 非同期処理を搭載している
例えば、その関数の1つとして setTimeout()やsrcでスクリプトをロードする関数 loadScript(src) などもある
function loadScript(src) {
// <script> タグを作りページに追加する
// 指定された src のスクリプトの読み込みを開始し、完了時に実行
let script = document.createElement('script');
script.src = src;
document.head.append(script);
}
新たに作成された 「script src="…"」 をドキュメントに挿入し、ブラウザは自動的に読み込みを開始し、完了時に実行する
- Callback の中の callback
2つのスクリプトを順次読み込む方法は、最初の1つを読み込み、2つ目はその後に読み込む事にである
2つ目の loadScript 呼び出しを callback の中に置くことになる
すると外側の loadScript完了後、そのコールバックは内側の loadScript を開始するので
loadScript('/my/script.js', function(script) {
loadScript('/my/script2.js', function(script) {
loadScript('/my/script3.js', function(script) {
// ...すべてのスクリプトが読み込まれるまで続く
});
})
});
このようにloadScriptでコールバックがずっと続く
これを「コールバック地獄」や「破滅のピラミッド」という
この非同期処理はコードが右に伸びていく訳だが、すぐに制御不能になるので、このコーディング方法は良くない
感想
非同期処理の処理を書く時は、「コールバック地獄」になる書き方をしないように注意する。
11.2 Promise
知らない単語
- Promiseとは
- 非同期処理の操作が完了したときに結果を返すもの
学んだこと
コールバック地獄にならない為にPromiseを使用する
同期処理のサポートをする
- executor関数の例
let promise = new Promise(function(resolve, reject) {
// promise が作られたとき、関数は自動的に実行される
// 1秒後、ジョブが "done!" という結果と一緒に完了したことを合図する
setTimeout(() => resolve("done!"), 1000);
});
上記でわかる事は
executor はnew Promiseとして自動で即座に呼ばれる
そしてresolve と reject は JavaScriptエンジンにより定義済みの関数なので、作成する必要はない
処理が始まると、executor は resolve("done") を呼び出し promise オブジェクトの状態が変わる
Promiseでresolveの値が返る時はresultになり、Promiseは絶対に値を返す
- then
.thenは最も重要で基本である
書き方は
promise.then(
function(result) { /* 成功した結果を扱う */ },
function(error) { /* エラーを扱う */ }
);
となり
.then の最初の引数は、promise が解決(resolve)されたときに実行され、結果を受け取る
.then の2つ目の引数は、promise が拒否(reject)されたときに実行され、エラーを受け取りる
- finally
try {...} catch {...} に finally があったが、Promise にも finally はある
new Promise((resolve, reject) => {
/* 時間のかかる処理を行い、その後 resolve/reject を呼び出す */
})
// 成功か失敗かは関係なく、promise が確定したときに実行される
.finally(() => 読込中のインジケータを停止する )
// よって、読み込み中のインジケータは結果/エラーを処理する前に必ず停止する
.then(result => 結果を表示する, err => エラーを表示する
try {...} catch {...}の時と処理は同じだが、書き方が違う
感想
PromiseはJavaScriptの中でもかなり重要だと思う。
.thenやtry {...} catch {...} でも同じ処理だった finallyなど、よく使用すると思われるものをしっかり覚えておきたい。
11.3 Promise チェーン
知らない単語
- ハンドラとは
- コンピュータプログラムやその一部分で、何らかの処理要求が発生したときに起動されるものを指す
学んだこと
Promise チェーンはコールバック地獄を解消できる
Promiseを使って.thenでつなぐようなイメージ
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1
return result * 2;
}).then(function(result) { // (***)
alert(result); // 2
return result * 2;
}).then(function(result) {
alert(result); // 4
return result * 2;
});
この考え方は、結果が .then を通じて渡されるという事である
ここでの流れは
最初の promise は1秒で解決される
その後、.then ハンドラが呼ばれる
返却された値は次の .then ハンドラへ渡される
となる
- fetch
リモートサーバからユーザに関する情報をロードすのに fetch メソッドを使う
let promise = fetch(url);
ここではurl へネットワークリクエストを行い、promise を返し、promise はリモートサーバがヘッダーで応答するとき、レスポンスがダウンロードされる前に response オブジェクトで解決される
完全なレスポンスを受け取るには、response.text() メソッドを使う必要がある
これはテキストがリモートサーバからダウンロードされたときに解決され、そのテキストを結果とする promise を返す
感想
Promise チェーンのイメージはわかったが、fetchの処理は難しい。結局はresponse.text()を使う必要があるのかもう少し調べて理解したい。
11.4 Promise でのエラーハンドリング
知らない単語
- エラーハンドリングとは
- エラーを処理すること、またはエラー処理を行うプログラム中のルーティン
学んだこと
.catch はすべての Promise の拒否を処理する
エラーを処理したい場所に正確に .catch を置く
fetch('https://no-such-server.blabla') // rejects
.then(response => response.json())
.catch(err => alert(err)) // TypeError: failed to fetch
上記のように.catch は直後である必要はなく、.then の後に来る可能性もある
もしエラーから回復する方法がないのであれば、.catch を使わなくても良い
エラーを追跡し、ユーザがそれに気づくためにイベントハンドラを持つべきである
感想
エラーをハンドリングできる.catchはとても便利だと思うし、コードを書く上でエラーを無くすのは重要なので上手く.catchを使っていきたい。
最後に
JavaScriptの中でもかなり重要なPromiseを学んだが、特に.catchはエラーハンドリングの為、よく使用すると思うので、もう1回詳しい人に解説してもらいながら復習する。