• トップ
  • ブログ一覧
  • 【JavaScript】 async/await で非同期処理をわかりやすく記述する
  • 【JavaScript】 async/await で非同期処理をわかりやすく記述する

    メディアチームメディアチーム
    2019.05.23

    IT技術

    async/await という仕組み

    本記事では、 JavaScript の非同期処理を扱うための async/await という仕組みについて取り上げます。

    async/await は、 Promise を利用した非同期処理をよりわかりやすく記述できます。

    本題へ進む前に Promie の復習をしておきましょう!

    Promise の復習

    async/await は、 Promise の仕組みを利用しているため、Promise への理解が大切だからです。

    非同期関数を作成するときに Promise が利用できます。

    1return new Promise((resolve, reject) => {});

    Promiseの状態

    Promise には、以下の状態があります。

    apending: 初期状態、実行中。成功も失敗もしていない
    fulfilled: 処理が完了した状態。then メソッドを呼び出す
    rejected: 処理が失敗した状態。then メソッドと catch メソッドを呼び出す。

    1. resolve() が呼ばれると、fulfilled の状態になります。
    2. rejected() が呼ばれると、rejected の状態になります。

    Promise の then

    then()  は、 Promise のインスタンスの状態が fulfilled となったときに実行する関数を登録できるインスタンスメソッドです。

    then() は、以下のように定義されています。

    1Promise.prototype.then(onFulfilled, onRejected)
    1. onFulfilled : fulfilled の状態のとき(resolve が呼ばれたとき)に実行される関数
    2. onRejected : rejected の状態のとき(reject が呼ばれたとき)に実行される関数

    Promise の catch

    reject()  とは、 Promise のインスタンスの状態が rejected となったときに実行する関数を登録するインスタンスメソッドです。

    1Promise.prototype.catch(onRejected)
    1. onRejected : rejected の状態のとき(reject が呼ばれたとき)に実行される関数

    Promise の関連記事

    featureImg2019.05.22【JavaScript】Promise で非同期処理を記述するはじめに~同期処理と非同期処理について~本記事では JavaScript の非同期処理を扱うための Promiseとい...

    async とは

    非同期関数を定義する関数定義です。

    async function で非同期関数を定義できます。

    async function は Promise インスタンスを返却します。

    1async function asyncFunc(/*引数*/) {
    2  // 処理
    3  return value;
    4}

    上記の例で定義した asyncFunc は、以下の関数と同義です。

    1// 通常関数で Promise を返却する
    2function promiceFunc(/*引数*/) {
    3  return new Promise((resolve, reject) => {
    4    resolve(value);
    5  });
    6}

    async function は、以下のことを行います

    1. async function 中でreturn されたとき、戻り値で Promise.resolve します
    2. async function 中でPromisereturn されたときは、その返り値のPromiseをそのまま返します。
    3. async function 中で例外や値を throw したときは、その値で Promise.reject をします。

    async function を実際に使ってみる

    async function を実際に書いてみて、どのように動作するか確認していきましょう。

    async function の中で return

    async function で定義された関数内でreturnされると、その return の返り値で Promise.resolve が実行されます。

    つまり、 async function で定義された関数は then() でつなげることができます。

    1async function asyncFunc() {
    2  // 処理
    3  return "resolve";
    4}
    5
    6// then() で return の値を受け取ることができる
    7asyncFunc().then(value => console.log(value)); // => resolve

    async function の中で Promise を return

    async function で定義された関数内で Promisereturn すると、そのPromise がそのまま返却されます。

    ですので、then()  や catch()  で処理を受けることができます。

    1async function rejectFunc() {
    2    // reject を呼ぶ
    3  return Promise.reject(new Error("エラーです"));
    4}
    5
    6rejectFunc().catch(error => console.log(error.message));
    7// 実行結果 => エラーです

    aysnc function の中で throw

    async function で定義された関数内で throw された場合、その throw の値でPromise.reject が実行されます。

    ですので、catch() でエラー処理を行えます。

    1// throw
    2async function throwFunc() {
    3  throw new Error("エラーが発生");
    4}
    5
    6// catch() で例外処理を行える
    7throwFunc().catch(error => console.log(error.message)); // => エラーが発生

    上記の例で見てもらったらわかるのですが、結局 async functionPromise を返すだけの関数です。

    外部の関数からaync function を呼び出すときは、Promise を返却する非同期関数であると認識していればいいです。

    await 式

    await 式の使いかた

    async function では、awaitを利用できます。

    await 式を利用すると、指定した Promise の結果が返されるまで処理を待機します。

    Promise の状態が、 fulfilled または rejected になると、処理を続行します。

    1// setTimeout を Promise でラップ
    2const wait = ms => new Promise(resolve => setTimeout(() => resolve(), ms));
    3
    4// async
    5async function awaitFunc() {
    6  console.log(1);
    7  await wait(3000); // Promise が返ってくるまで awaitで 処理停止
    8  console.log(2); // 約3秒経過に表示
    9}
    10
    11awaitFunc();

    wait()  関数は、setTimeout()  を Promise でラップした非同期関数です。

    そのため、普通は wait()  関数で処理が止まることはありません。

    await式を利用しなければ、ターミナルにはすぐに「1 2」と表示されます。

    ですが、await 式を利用しているため、上記のコードではwait() 関数が処理を終了するまで、次の処理 (console.log(2) ) が評価されません。

    ですので、上記のコードを実行してみると、コンソールに 1 が表示されてから、約 3 秒経過した後、2 が表示されます。

    await 式の嬉しいところ

    await 式を利用することで、非同期処理でありながら、同期処理のように上から順番に処理するように記述できます。

    非同期関数でありながら、見通しのよいコードが記述可能となります。

    1// 約一秒後、受け取った値を2倍で返す非同期関数
    2const promiseFunc = value => {
    3  return new Promise((resolve, reject) => {
    4    setTimeout(() => {
    5      resolve(value * 2);
    6    }, 1000);
    7  });
    8};
    9
    10// 非同期関数
    11async function asyncFunc() {
    12  // await 式によって順番に処理される
    13  const a = await promiseFunc(1); 
    14  const b = await promiseFunc(2);
    15  const c = await promiseFunc(3);
    16
    17  return a + b + c;
    18}
    19
    20asyncFunc().then(value => {
    21  console.log(value); // => 12
    22});

    サンプルコード中の関数 promiseFunc()  は、処理に約1秒かかります。

    asyncFunc()  では await 式で処理が終了するのを待機して、各変数に格納しています。

    await 式の利用によって同期処理のような記述になっています。

    ※ 1つずつ関数の終了を待っているため、このコードは実行に約3秒以上かかります。このままでは問題なので、次の章で効率的に処理できるよう改善します。

    async の中で Promise.all()

    await を使うと、非同期処理を同期処理のように記述できわかりやすいです。

    ですが、連続で記述された非同期処理を1つずつ待ってしまってはムダな待ち時間が発生します。

    その問題を解決するには、 Promise.all()  というメソッドを利用します。

    Promise.all()

    Promise.all()  は、引数に非同期関数の配列を受け取り、Promise インスタンスを返すメソッドです。

    非同期処理の結果は、配列として戻ってきます。

    Promise.all() に引数として与えられた非同期関数の配列は、並行して実行されます。

    そのため、非同期処理を効率的に処理できます。

    すべての非同期関数の処理が終わるとPromise.all() で返ってくる Promise インスタンスの状態は fulfilled になります。

    await 式は返された Promise インスタンスが fulfilled になるまで待機します。

    つまり await 式は、Promise インスタンスを返却する処理と組み合わせることができます。

    よって await 式は、 Promise.all() メソッドとも組み合わせることができます。

    サンプルコード

    1const promiseFunc = value => {
    2  return new Promise((resolve, reject) => {
    3    setTimeout(() => {
    4      resolve(value * 2);
    5    }, 1000);
    6  });
    7};
    8
    9// 非同期関数
    10async function asyncFunc() {
    11  // 並行して処理が実行され、全ての処理が終わるまで待機
    12  const values = await Promise.all([
    13    promiseFunc(1),
    14    promiseFunc(2),
    15    promiseFunc(3)
    16  ]);
    17
    18  console.log(values);
    19}
    20
    21asyncFunc();

    [結果]
    [ 2, 4, 6 ]

    サンプルコード中の関数 promiseFunc()  は処理に約1秒かかり、他の変数やデータベースに影響を与えない独立した関数です。

    並行して処理を行っても問題がないので、Promise.all()  で実行しています。

    そして、実行結果を await 式で受け取って、console.log  でターミナルに表示しています。

    実行してみると、約1秒で処理が終了します。

    前の章では同じような処理に3秒かかっていたので、改善できています。

    このように、互いに影響を及ぼさない非同期処理は、Promise.all()  を利用することで効率的に処理できます。

    await 式の中で例外

    await 式の中で、 Promiserejected になった場合、その場でエラーを throw します

    async function 内で発生した例外は自動的にキャッチされます。

    何も処理しなければ、async functionrejectedPromise を返します。

    サンプルコード

    以下サンプルコードです。

    1async function asyncFaile() {
    2  // await 式で例外発生
    3  const value = await Promise.reject(new Error("エラーです"));
    4}
    5
    6// asyncFunc は例外を自動的にキャッチ
    7asyncFaile().catch(error => console.log(error.message)); // => エラーです

    エラーが throw されるので try...catch でエラーをキャッチすることもできます。

    1async function asyncFaile() {
    2  // await 式で例外発生
    3  try {
    4    const value = await Promise.reject(new Error("エラーです"));
    5  } catch (error) {
    6    console.log(error.message); // => エラーです
    7  }
    8}
    9
    10// asyncFaile の中で 例外処理がされているので、catch()は呼ばれない
    11asyncFaile()
    12  .then(() => console.log("呼ばれる")) // => 呼ばれる
    13  .catch(error => console.log("呼ばれない"));

    async function 内ではエラーを throw していないので、fulfilledPromise が返されることになり、asyncFaile().then は呼ばれますが、asyncFaile().catch は呼ばれません。

    もし、上位の関数でエラー処理したい場合はcatchしたのち、再び throw してください。

    1async function asyncFaile() {
    2  // await 式で例外発生
    3  try {
    4    const value = await Promise.reject(new Error("エラーです"));
    5  } catch (error) {
    6    throw error;
    7  }
    8}
    9
    10// asyncFaile で エラーが throw されるので、catch で受ける。
    11asyncFaile()
    12  .then(() => console.log("正常処理")) // 呼ばれない
    13  .catch(error => console.log(error.message)); // => エラーです

    async/await の対応状況

    Chrome、Firefox、Edge、Safari といった最新ブラウザは対応済みです。

    IE11 は未対応です。

    ECMAScript 2016+ compatibility table

    もし、IE もサポートに含めるのなら、Babel でトランスパイルする必要があります。

    さいごに

    以上、async/await についてでした。

    それでは、最後に、今回の内容をまとめてみます!

    1. async/await 式で非同期処理を扱える
    2. async function で定義された関数は、Promise インスタンスを返す
    3. await 式は、Promise インスタンスが fulfilled の状態になるまで、次の処理を評価しない
    4. await 式は、Promise.all のような Promise インスタンスを返す処理と組み合わせることができる

    非同期処理の async/await についてでした。

    本記事が JavaScript のコード作成のお役にたてたら幸いです。

    こちらの記事もオススメ!

    featureImg2020.08.07JavaScript 特集知識編JavaScriptを使ってできることをわかりやすく解説!JavaScriptの歴史【紆余曲折を経たプログラミン...

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background