4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

非同期処理 Promise/async/await について簡単に説明【JavaScript】

Posted at

はじめに

JavaScriptに関する「非同期処理」「Promise」「async/await」について簡単にまとめました。

初心者の方にとっては少しとっつきにくいお話だと思いますが、こんな感じか〜くらいに理解してもらえれば嬉しいです。

概要

  • 非同期処理とは(同期処理との比較)
  • Promise
  • async/await

非同期処理とは

非同期処理は字の如く「同期的ではない処理」となります。

同期的な処理とは、

  1. Aの処理
  2. Bの処理
  3. Cの処理

と処理が順番に書かれている場合、上から順に処理が実行され、AとBの処理が完了した後にCの処理が実行されるようなものになります。

例えば、

console.log('A');

// 3秒後に実行する処理
setTimeout(() => {
  console.log('B(3秒後に実行)');
}, 3000);

console.log('C');

このような処理があった時、A→3秒後にB→Cの順に処理されることが理想ですが、実際に動かしてみると

A
C
B(3秒後に実行)

というような順番で処理されています。

JavaScriptは非同期言語であり処理を待つことができないため、前の処理に時間がかかった場合は、その処理の完了を待たずに後続処理が行われてしまうことが理由です。

ですが、後述するPromiseasync/awaitを使うことでこのような非同期処理を同期的に扱うことができるようになります。(もちろん非同期的に処理させることも可能です)

Promise

Promiseは日本語に訳すと「約束」となりますが、Promiseを使うことで処理の順序や後続処理をあらかじめ指定(約束)できるものだと考えてもらえれば大丈夫です。

下記のコードでは、new Promiseの第一引数のコールバック(ここではメソッドの引数に渡す関数のこと)の処理でsetTimeout()を実行してます。

処理が完了したことを知らせるために処理の最後にresolve()を実行します。
こうすることで、resolve()が実行された後に、メソッドチェーンで処理されているthen()メソッドの中身が実行されるようになります。

console.log('A');

// お約束を取り付けたい処理にPromise
new Promise((resolve) => {
    // 3秒後に実行する処理
    setTimeout(() => {
        console.log('B completed.');
        // 処理の完了を知らせる
        resolve('');
    }, 3000);
}).then(() => {
  // Bの処理の完了を待ってから実行される処理
  console.log('C');
});

A
B completed.
C

Promiseには状態がある

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

  • pending
    • 未解決で、処理が完了するのを待っている状態
  • resolved
    • 解決済みで、処理が問題なく完了した状態
  • rejected
    • 拒否されたことで処理が失敗してしまった状態

以下に3パターンのコードを記述します。

pending

const promise = new Promise((resolve) => {});

console.log(promise);

結果
スクリーンショット 2023-08-22 1.18.58.png

この状態だとまだPromiseに対して何もしていないため、pendingになります。

resolved

const promise = new Promise((resolve) => {
    resolve('ここのメッセージが出るよ');
}).then((resolve) => {
    console.log(resolve);
});

結果

ここのメッセージが出るよ

resolveさせることができました。

また、resolve()メソッドの引数に渡した値は、then()のコールバック関数の引数として受け取ることができます。

rejected

Promiseインスタンスの第二引数にはrejectを指定することができます。

const promise = new Promise((resolve, reject) => {
    reject('エラー');
}).then((resolve) => {
    console.log(resolve);
}).catch((reject) => {
    console.log(reject);
});

結果

エラー

rejectさせることができました。

余談ですが、Promise.all()というメソッドがあり、引数にPromiseの配列を渡すことで、全ての処理がresolveしてから次の処理に進むといったことを実現できます。

const promiseA = new Promise((resolve) => {
        setTimeout(() => resolve('promiseA completed.'), 3000);
});

const promiseB = new Promise((resolve) => {
        setTimeout(() => resolve('promiseB completed.'), 7000);
});

const promiseC = new Promise((resolve) => {
        setTimeout(() => resolve('promiseC completed.'), 5000);
});

Promise.all([promiseA, promiseB, promiseC])
    .then((resolve) => {
        console.log(resolve);
    });

結果
スクリーンショット 2023-08-22 1.51.29.png

結果も各Promiseのresolveを配列で受け取れます。

async/await

  • async
    • 非同期関数を定義する時に使用。関数の頭につけるとPromiseを返す関数を定義することができる。
  • await
    • Promiseオブジェクトが値を返すのを待つための演算子。
    • async関数内でしか使用できない。

async

下記のように非同期関数を定義する時に使用し、戻り値の型がPromise型になります。
Promise型はジェネリクスとなっているため、resolve()の引数に渡す型と揃えるようにします。

const fetchData = async (): Promise<string> => {
  return new Promise((resolve) => {
      resolve('Data fetched!');
  });
}

上記で定義したfetchDataというasync関数を実行していきたいと思いますが、
まずはawaitをつけずに実行してみます。

const data = fetchData();
console.log(data);

スクリーンショット 2023-08-22 2.00.22.png
結果はこのようにpendingとなります。

Promiseで返ってきた値をそのままthen()することで、resolveさせることができます。

const data = fetchData();
data.then((resolve) => console.log(resolve));

スクリーンショット 2023-08-22 2.02.43.png

では次に、awaitをつけて実行します。

const data = await fetchData();
console.log(data);

スクリーンショット 2023-08-22 2.02.43.png
awaitをつけて実行することで、Promiseによって約束されている戻り値がそのまま返ってくるため、data変数から直接参照することができました。




こんなようにasync/awaitを使うことで、Promiseを使用した処理でthen()などを使用せずに、非同期処理を簡潔に書くことができます。

try catchによるエラーハンドリングも書き方にいくつかパターンがあり、

try {
    await fetchData();
} catch (error: Exception) {
    console.log(error);
}

といったシンプルな書き方や、

await fetchData().catch((error) => {
    console.log(error);
})

こんな感じで非同期処理ごとにエラーハンドリングを定義することもできます。
この辺は好みですね。

最後に

個人的にも曖昧だった非同期処理について見直す良い機会になったと思います。
最後まで読んでいただきありがとうございました。

参考記事

async/await 入門(JavaScript)
【ES6】 JavaScript初心者でもわかるPromise講座

Promiseの状態についてもっと詳しく知りたい方は↓
fulfilled, rejected, settled... Promiseの用語、全部分かりますか?

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?