• 最終更新日:

【JavaScript】asyncとawaitを使った非同期処理の方法 - Promiseとの違いとは?

【JavaScript】asyncとawaitを使った非同期処理の方法

今回はJavaScriptの非同期処理で使うasyncとawaitについて説明します。

前提条件は以下です。

  • 同期処理と非同期処理の違いをざっくり理解している
  • Promiseについてざっくり理解している

説明する環境は以下です。

  • macOS Catalina v10.15.5
  • Visual Studio Code v1.57.0
この記事の目次

asyncとawaitを使った非同期処置の方法

非同期処理の1つにPromiseがありますが、asyncとawaitと組み合わせて使うことが多いです。同期処理と非同期処理の違いについて、それからPromiseについては以下の記事をどうぞ。

asyncの基本的な使い方

関数の前にasyncをつけることで、非同期処理することを宣言しています。

const hogehoge = async (num) => { //非同期処理ですよ、と宣言する
  return num;
};

そして以下のように、asyncがついた関数をコンソールで確認するとPromiseが返ってきているのがわかります。

const hogehoge = async (num) => {
  return num;
};

console.log(hogehoge(1)); // Promiseが返ってくる

hogehoge関数に指定した引数は、returnによってPromiseにあるresolveメソッドで受けて実行されるので、thenで処理をつなげることができます。

const hogehoge = async (num) => {
  return num;
};
hogehoge(1).then((result) => {
  console.log(result + 2); //3 が表示される
});

hogehoge関数内でエラーがある場合は、Promiseにあるrejectメソッドが実行されるので、catchで受けることもできます。

const hogehoge = async (num) => {
  throw new Error("エラーです");
};
hogehoge(1).catch((err) => {
  console.log(err); // Error:エラーです が表示される
});

asyncをまとめると以下

  • 関数の前にasyncとつけて非同期処理を宣言する
  • asyncの関数を呼びだすとPromiseを返す
  • asyncがある関数にreturnがあるとresolveメソッドを実行
  • asyncがある関数にエラーがあるとrejectメソッドを実行

awaitの基本的な使い方

awaitはasyncがある関数の中でしか使えません。そしてawaitはPromiseの結果が返ってくるまで、asyncがある関数の処理自体を一時停止します

const ahoaho = (x) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x);
    }, 3000);
  });
};

const hogehoge = async (num) => {
  const value = await ahoaho(10);
  console.log(value + num);
};
hogehoge(100); //3秒後に110と表示される

hogehoge関数を実行すると、awaitがあるahoaho関数の結果が返ってくるまで、hogehoge関数の処理を一時停止します。ahoaho関数から10秒後に10が返ってきたら、hogehoge関数を再度実行します。

ちなみにasyncとawaitを使わずにPromiseだけで同じ内容を書くと以下です。

const ahoaho = (x) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x);
    }, 3000);
  });
};

ahoaho(100)
  .then((value) => {
    return value + 10;
  })
  .then((answer) => {
    console.log(answer); //3秒後に110と表示される
  });

処理をthenつなげるために、returnで値を次のthenの引数に渡しています。Promiseだけで書くと処理が増えてくるとコードが複雑になりがちですが、asyncとawaitをまぜることでスッキリと書くことが可能になります。

awaitの後に必ずPromiseは必要?

awaitの後は、returnされたPromiseがある関数を指定することが多いですが、無くても問題ありません。awaitのあとにPrimise以外のものがあれば、 resolveメソッドが実行された状態になります。

const hogehoge = async (num) => {
  const value = await 10;
  return value;
};
hogehoge(100).then((y) => {
  console.log(y); //10 と表示される
});

awaitをまとめると以下

  • awaitは、asyncがある関数の中でしか使えない
  • awaitはPromiseからの結果を待ちます。その間、asyncがある関数自体も一時停止。
  • awaitの次に指定できるのはPromiseだけとは限らない

asyncとawaitを使った直列実行と並列実行の使い方

非同期処理を1つ1つawaitで実行すると直列実行になります。複数の非同期処理を一括して実行すると並列実行になります。

それぞれ具体的に説明します。

直列実行の場合

awaitで返ってきた値を、次の処理でも使いたい場合は直列実行をします。

const ahoaho = (x) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x * 2);
    }, 1000);
  });
};

const hogehoge = async (num) => {
  const value = await ahoaho(10); //順番に処理
  const value2 = await ahoaho(value); //順番に処理
  const value3 = await ahoaho(value2); //順番に処理
  return value3 + num;
};
hogehoge(100).then((y) => {
  console.log(y); // 180が表示される
});

並列実行の場合

それぞれの非同期処理がお互いに値のやりとりが必要ない場合は並列実行が使えます。

  • Promise.all … 並んでいる非同期処理すべてが完了したら、次の処理を実行する
  • Promise.race … 並んでいる非同期処理の1つでも完了したら、次の処理を実行する

Promise.allの例

Promisw.allの中にある値がすべて取得できたら、次の処理を実行します。

Promise.allの中にある3つの関数の値 + 変数numが揃うまではhogehoge関数を1時停止して、揃った段階でreturnでthenに配列を渡しています。

const ahoaho = (x) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x * 2);
    }, 1000);
  });
};

const hogehoge = async (num) => {
  return await Promise.all([ahoaho(10), ahoaho(6), ahoaho(2), num]);
};

hogehoge(100).then((arr) => {
  const reducer = (sum, currentValue) => sum + currentValue;
  const value = arr.reduce(reducer);
  console.log(value); //136 と表示される
});

Promise.raceの例

Promise.raceの中び値が1つでも取得できれば、すぐに次の処理を実行します。

以下のようにbokeboke関数はahoaho関数より1秒早く実行されます。そのため、bokeboke関数から値を受け取った段階で、すぐにthenにその値を渡しています。ahoaho関数の値が返ってくるのは待ちません。

const ahoaho = (x) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
};

const bokeboke = (x) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(x * 2);
    }, 1000);
  });
};

const hogehoge = async (num) => {
  return await Promise.race([ahoaho(num), bokeboke(num)]);
};

hogehoge(5).then((y) => {
  console.log(y);  //10 が表示される
});

さいごに

今回はJavaScriptで使うasyncとawaitを使った非同期処理の方法について説明しました。APIをからデータを取得して何かを処理したり、作業自体に時間がかかるものに使います。

asyncとawaitを使うにはPromiseの理解が必要です。学習がまだであれば以下の記事を参考にしてみてください。

実際にAPIからasyncとawaitを使って情報を取得する方法については以下をどうぞ。