🦁

【自分用メモ】コールバック関数->Promise->async/awaitを学ぶ

2025/03/03に公開1

はじめに

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);

以下の動画で使われているコードを引用
https://www.youtube.com/watch?v=kbKIENQKuxQ

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