🦁
【自分用メモ】コールバック関数->Promise->async/awaitを学ぶ
はじめに
Reactを学習していて、async/awaitを復習したため自分用にメモを残します。
前提 : JSは処理の完了を待たない
JSは時間がかかる処理があってもその処理の完了を待たずに、次の処理に移ります
console.log("開始!")
setTimeout(() => {
console.log("10秒経ちました!");
}, 10000);
console.log("終了!")
//出力結果
開始!
終了!
10秒経ちました!
「開始!->10秒経ちました!->終了!」と出力したいのに先に終了されてしまいました泣
setTimeout
という時間がかかる処理の後に特定の処理を行いたい、と思った場合に後述するコールバック関数、Promise, Async/Awaitを使います。
より抽象化すると「時間がかかる処理を待ってから特定の処理を行いたい」という要望に対して生まれたのがコールバック関数、Promise, Async/Awaitです。
今回は時間のかかる処理でsetTimeout
を使いましたが、実際にはAPI実行等が多いです。
API実行はDBサーバーとの連携が発生するため時間がかかるためです。
コールバック関数の誕生と問題点
コールバック関数=「関数の引数に別の関数を指定する処理」
以下のような感じです。
const timer = (callback) => {
setTimeout(() => {
console.log("10秒経ちました!");
callback()
}, 10000);
};
const showEndMessage = () => {
console.log("終わりー")
}
timer(showEndMessage)
しかし、コールバック地獄=「処理がネストして読みづらいコードになる」 という問題も出てきました。
カウントダウンタイマー(3->2->1)を実装してみるとネストして読みづらい涙
setTimeout(() => {
console.log(3);
setTimeout(() => {
console.log(2);
setTimeout(() => {
console.log(1);
}, 1000);
}, 1000);
}, 1000);
以下の動画で使われているコードを引用
Promiseの誕生と問題点
Promise=「時間がかかる処理の待機や、その処理結果に応じて別の処理をすることを約束する」
処理が完了しているか否かをステータスで表現します。
- pending:初期状態。成功も失敗もしていません。
- fulfilled:処理が成功して完了したことを意味します。
- rejected:処理が失敗したことを意味します。
//timerはPromiseオブジェクトを返す関数。
const timer = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("10秒経ちました!");
resolve();
}, 10000);
});
};
const showEndMessage = () => {
console.log("終わりー");
};
//timer関数実行後にPromiseオブジェクトのステータス
timer().then(showEndMessage);
ただ、Promiseオブジェクトを使ってもthen/catchを繋げると可読性がよくない問題点がありました。
async/await
Promiseオブジェクトの受け渡しを利用しつつ、同期処理のようにコードを書けるようにしたのがasync/awaitになります。
- async functionはPromiseを返す
- async : awaitキーワードを使っている関数のあたまに付ける必要がある
- await : Promiseの値が取り出されるまで待つ
Discussion
new Promise() をすると ネストが発生するので Promise.withResolvers() で浅く維持するのも手ですね。