Suzuki Blog Written by Yuki Suzuki

JavaScriptの非同期処理について【コールバック関数、Promise、async/await】

JavaScript

今回はJavaScriptの非同期処理についてまとめます。

記事の内容

  • 同期処理とは
  • 非同期処理とは
  • コールバック関数
  • Promise
  • async/await

スポンサードサーチ

同期処理とは

まず同期処理とは、コードを上から順番に処理していき、ひとつの処理が終わるまで次の処理は行われない処理のことです。

そのため、「データを取得し、画面に表示する」という処理をする場合、データの取得が終わるまで画面には何も表示されません。

そこで、「データを取得している間に、画面を作成しておく」というようにバッググラウンドで処理する方法が登場しました。

これが「非同期処理」です。

非同期処理とは

改めて非同期処理とは、時間がかかる処理の完了を待たずに次の処理を進め、同時に複数の処理を進める処理のことです。

ファイルの読み込み、ネットワークの通信処理、データベースへのアクセスなどは非同期処理で行うことが多いです。

非同期処理を書く方法は3つあります。

  • コールバック関数
  • Promise
  • async/await

この3つは書き方は違いますが、本質的には同じことをしています。

(時間がかかる処理の完了を待たずに次の処理を進め、同時に複数の処理を進める)

非同期処理を行うために、最初に登場したのが「コールバック関数」です。

スポンサードサーチ

コールバック関数

関数の引数に渡された関数をコールバック関数と言います。

「○○が終わったら、✗✗やってね」の【✗✗やってね】の部分がコールバック関数です。

// コールバック関数
setTimeout(() => console.log('コールバック関数を実行!'), 1000);
console.log('先に行くよー');
先に行くよー
コールバック関数を実行!

コールバック地獄

コールバック関数の登場により、非同期処理を実現できました。

ただし、コールバック関数を順々に実行しようとするとネストしなければならず、読みにくいコードになっていきます。

// コールバック関数を使ったカウントダウン
setTimeout(() => { // コールバック地獄
  console.log(3);
  setTimeout(() => {
    console.log(2);
    setTimeout(() => {
      console.log(1);
    }, 1000);
  }, 1000);
}, 1000);

console.log('先に行くよー');
先に行くよー
3
2
1

これを解消するために「Promise」が登場します。

Promise

// Promiseを使ったカウントダウン
new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log(3);
    resolve(); // resolveが呼ばれたとき、thenのコールバック関数が実行される
    // reject('error!') // rejectが呼ばれたとき、catchでエラーハンドリングできる
  }, 1000);
}).then(() => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(2);
      resolve();
    }, 1000);
  });
}).then(() => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(1);
      resolve();
    }, 1000);
  });
}).catch((e) => {
  console.log(e);
})

console.log('先に行くよー');
先に行くよー
3
2
1

Promiseではthenを繋ぐことができます。(thenチェイン)

それにより、ネストをせずにフラットに書くことができます。

また、Promiseには3つの状態があります。

  • pending:初期状態

  • fulfilled:処理が成功して完了した状態
  • 
rejected:処理が失敗して完了した状態

new Promise()でPromiseオブジェクトが作られた時点では、pendingの状態です。

その後、resolveが呼ばれるとfulfilledに変化し、thenに書かれた処理が実行され、rejectが呼ばれるとrejectedに変化し、catchに書かれた処理が実行されます。

Promiseの登場により、非同期処理をネストさせずフラットに書けるようになりました。

とはいえ、まだ読みにくいです。

もっと普通に(同期処理っぽく)書くために登場したのが「async/await」です。

スポンサードサーチ

async/await

// async/awaitを使ったカウントダウン
func = async() => { // asyncで囲むことで、これは非同期処理であると宣言できる
  await log(3); // awaitをつけることで、Promiseの結果が返ってくるまで待つことができる
  await log(2); // thenで繋げなくても、連続した処理を普通に書けるようになる
  await log(1);
};

log = (num) => { // Promise関数
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(num);
      resolve();
    }, 1000);
  });
};

func();

console.log('先に行くよー');
先に行くよー
3
2
1

まず同期的に処理したい部分をasyncで囲みます。

そして同期的に処理したいPromise関数にawaitをつけます。

これによりPromiseの処理が終わるまで待ってくれるようになり、thenで繋げなくても連続した処理を普通に書けるようになります。

まとめ

  • 非同期処理とは、時間がかかる処理の完了を待たずに次の処理を進め、同時に複数の処理を進める処理のこと
  • 非同期処理には3つの書き方がある
  • コールバック関数(コールバック地獄がつらい)
  • Promise(thenで繋ぐのがつらい)
  • async/await(普通に書ける)

コーディングを学ぶならデイトラ

Web制作コース

動画でプログラミングを学ぶなら

Udemy

オンラインスクールでプログラミングを学ぶなら

Skill Hacks