現在、もりけん塾で
マークアップエンジニアの方がフロントエンドエンジニアになる為の課題に取り組んでいます。
今回は課題9・10の実装で学んだことをブログへまとめます。
課題9
課題7をasync / awaitを使用して実装する
resolveになるまでの間にloading画像をだして、終わったら除く。
これはサーバーから値が渡ってくるまではそれを出して、
渡ってきたら値を加工してhtmlとして書き出すを想定しています
async / await
async
Async Functionとは非同期処理を行う関数を定義する構文です。 Async Functionは通常の関数とは異なり、必ず
https://azu.github.io/promises-book/#chapter5-async-functionPromise
インスタンスを返す関数を定義する構文です。
async function fn() {
return ("こんにちは");
}
console.log(fn());
確認するとPromiseインスタンスを返していて、
returnした値をラップしていることも確認できました。
await
async内でのみ使用可能。
指定したPromiseが結果を返すまで、それ以降の処理を待機させることができる
async function hello() {
return ("こんにちは");
}
async function greeting() {
console.log("おはようございます");
const result = await hello(); //hello()のPromiseの結果を待つ→resultへ格納する
console.log(result);
console.log("こんばんは");
}
greeting();
// おはようございます
// こんにちは
// こんばんは
課題10
先程の課題にtry..catch..finallyを追加する。
try…catch…finally
try {
console.log("tryが実行");
throw new Error();
console.log("これは実行されない");
} catch (err) {
console.error(err + "エラーが発生しています");
} finally {
console.log("finallyが実行");
}
// tryが実行
// Errorエラーが発生しています
//finallyが実行
まずtryブロックが実行され、エラー(例外)がなければcatchは無視される。
tryの中でエラー(例外)が発生した場合はtryの実行が停止し、catchへ処理が移る。
finallyはエラーが起きても起きなくても実行される。
async/await と try/catch
以下のコードは、今回の課題を簡略化したものを例にしました
const fruitData = [ "apple", "banana", "orange"];
// 3秒後にresolveが実行され、fruitData が渡る
function getData() {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(fruitData);
}, 3000);
});
}
async function fetchData() {
try {
// getDataがPromiseの結果を返すまで待機し、返ったらresultに格納
const result = await getData();
console.log(result); // fruitDataが出力
} catch (e) {
console.log(e + "エラーが発生しました!")
} finally {
console.log("finally実行");
}
}
fetchData();
// ['apple', 'banana', 'orange']
// finally実行
エラーを投げた場合を考える
try内でエラーを投げてみる
結果は無事にエラーをキャッチしました
//省略
async function fetchData() {
try {
throw new Error("Error");
} catch (e) {
console.log(e + "エラーが発生しました!")
} finally {
console.log("finally実行");
}
}
fetchData();
//Error: Errorエラーが発生しました!
// finally実行
Promise内でエラーを投げてみた
結界はPromise内で投げたエラーをキャッチしませんでした
// 省略
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error("Error");
}, 3000);
});
}
async function fetchData() {
try {
const result = await getData();
console.log(result)
} catch (e) {
console.log(e + "エラーが発生しました!")
} finally {
console.log("finally実行");
}
}
fetchData();
//undefinedエラーが発生しました!
//finally実行
// Uncaught Error: Error
この様な結果になった理由を考えました。
まずPromise内でエラーが投げられた場合の状態を確認しました。
結果を返していない状態なので
catchへ処理が移行し「undefinedエラーが発生しました!」が出力、その後finallyが実行した。
Errorが投げられたら自然とrejectedな状態になるんじゃないの???
ちなみにsetTimeoutがない場合はcatchできた
//省略
function getData() {
return new Promise((resolve, reject) => {
throw new Error("エラーが発生!");
});
}
async function fetchData() {
try {
const result = await getData();
console.log(result)
} catch (e) {
console.log(e + "エラーが発生しました!")
} finally {
console.log("finally実行");
}
}
fetchData();
ここの挙動がいまいちよくわからず、苦しみました…
try…catchは同期的な例外をcatchするためsetTimeout内で発生した例外は、
非同期のためcatchできない、ということではないかと考えました。
課題で苦戦した点
そもそもなぜtry…catchを書くのかがわからず苦戦しました。
Promiseを返すのだから、thenとcatchじゃだめなのか?
なぜtry…catch(エラーハンドリング)が必要なのか
エラー(例外)が起きた時にユーザーに何らかの形で知らせる処理をしなければ
devツールを開かない限り、ユーザーは知ることができない。
何が起きたがわからないが、意図した挙動にならない….という状態を作らないため。
then catch / try catch
Async/awaitはPromiseのシンタックスシュガーであり、
同期的にコードを記述できるところが利点である。
そのため、try…catchを使用する方が
同期的に記述ができて、ネストも深くなりにくいのでコードがわかりやすい
try catch best practice
今回のレビューで頂いた点でもありますが
tryに処理を詰め込みすぎないことは重要だと学びました。
どこでエラー(例外)が起きたかをわかりずらくするからです。
また以下の記事も参考になりました。
トライ・キャッチは、エラーが発生する可能性がある場合に実装されるべきです。例えば、外部サービスが利用できない場合、ログイン認証が無効な場合、ユーザーの入力が無効な場合などです。可能な限り、特定のエラーをキャッチするように努めるべきです。
https://www.codemag.com/article/1807021/JavaScript-Corner-Try-Catch
エラーにはプログラマーのミス(変数の未定義や、引数の渡し忘れなど)とユーザーのミスがあり
プログラマーのミスに関しては修正を行わなけらばならないし、
それ以外の制御できないミスに関しては、コードでチェックさせ適切に処理する必要がある。
今回レビューをしてくださった
ちひろさん にゃっつさん ありがとうございました。
コメント