node v8以降の非同期処理はasync/awaitを使用しよう

こんにちは、北野です。

今回の投稿は
「node v8以降の非同期処理はasync/awaitを使用しよう」
です。

async/awaitとは?

async/awaitとはECMAScript2017で追加された、非同期構文です。
Promiseを同期処理と同じように扱えるようになるため、非同期処理がとても書きやすくなります。

asyncとは?

非同期関数(async function)を宣言するためのキーワードです。
関数宣言の先頭につけて使用します。
例)

async function AsyncFunction() {
}

非同期関数の戻り値はPromiseオブジェクトとなることに注意してください。

awaitとは?

非同期関数の中で、Promiseオブジェクトを同期的に扱うためのキーワードです。
Promiseオブジェクトの前につけて使用します。
例)

await new Promise((resolve, reject) => {
    resolve(10);
});

awaitは非同期関数の内でしか使用できないことに注意してください。

awaitで待機しているPromiseオブジェクトがFulfilled状態に遷移した場合、resolve()に渡された値は戻り値として受け取ることができます。
例)

(async function() {
  let result = await new Promise((resolve, reject) => {
    resolve(10);
  });
  console.log('result = ' + result);
})();

上記例の実行結果は
「result = 10」
となります

awaitで待機しているPromiseオブジェクトがRejected状態に遷移した場合、reject()に渡された値は例外として通知されます。
例)

(async function() {
  try {
    let result = await new Promise((resolve, reject) => {
      reject('エラーが発生しました');
    });
  } catch(e) {
    console.log(e);
  }
})();

上記例の実行結果は
「エラーが発生しました」
となります

Promiseオブジェクト以外にawaitを指定した場合は指定したものをそのまま戻り値として受け取ることができるようです。
例)

(async function() {
  let result = await 10;
    console.log('result = ' + result);
})();

上記の実行結果は
「result = 10」
となります。

async/awaitを使用したサンプルコード

動作確認環境

今回の動作確認環境は以下になります。

実行環境 バージョン
nodejs v8.10

順次処理(直列処理)の成功例

async/awaitを使用した順次処理の成功例です。

サンプルプログラム実行3秒後コンソールに”LongTimeProcess1″を表示し、
その3秒後に”LongTimeProcess2″を表示します。
(長時間かかる処理をsetTimeout()を使用することで疑似的に表現しています。)
また、その後、関数からの戻り値を合計して表示しています。
例)

(async function AsyncFunction() {
  let result1 = await TimeoutFunction(1);
  let result2 = await TimeoutFunction(2);
  console.log('sum = ' + (result1 + result2));
})();

function TimeoutFunction(count) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('LongTimeProcess' + count);
      resolve(count);
    }, 3000);
  });
}

順次処理(直列処理)の失敗例

async/awaitを使用した順次処理の失敗例です。

サンプルプログラム実行3秒後コンソールに”LongTimeProcess1″を表示し、
例外ハンドラへ処理が移り”エラーが発生しました”が表示されます。
「let result2 = await TimeoutFunction(2);」
以降は実行されないことに注意してください。
例)

(async function AsyncFunction() {
  try {
    let result1 = await TimeoutFunction(1);
    let result2 = await TimeoutFunction(2);
    console.log('sum = ' + (result1 + result2));
  } catch(e) {
    console.log(e);
  }
})();

function TimeoutFunction(count) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('LongTimeProcess' + count);

      if (count === 1) {
        reject('エラーが発生しました');
      } else {
        resolve(count);
      }
    }, 3000);
  });
}

並列処理の成功例

async/awaitを使用した並列処理の成功例です。

サンプルプログラム実行3秒後コンソールに”LongTimeProcess1″を表示し、
その1秒後(サンプルプログラム実行4秒後)に”LongTimeProcess2″を表示します。
また、その後、関数からの戻り値を合計して表示しています。
例)

(async function AsyncFunction() {
  let resultAry = await Promise.all([
    TimeoutFunction(1, 3000),
    TimeoutFunction(2, 4000)
  ]);
  console.log('sum = ' + (resultAry[0] + resultAry[1]));
})();

function TimeoutFunction(count, waitTime) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('LongTimeProcess' + count);
      resolve(count);
    }, waitTime);
  });
}

並列処理の失敗例

async/awaitを使用した並列処理の失敗例です。

サンプルプログラム実行3秒後コンソールに”LongTimeProcess1″を表示し、
例外ハンドラへ遷移し”エラーが発生しました”を表示します。
Promise.all()をawaitで待機した場合、Promise.all()に指定したいずれかのPromiseオブジェクトがRejected状態に遷移した時点で、例外ハンドラへ処理が移ることを確認してください。
また、Promise.all()に指定した全てのPromiseオブジェクトは途中でキャンセルされたりしないため、”エラーが発生しました”を表示後に”LongTimeProcess2″を表示します。
例)

(async function AsyncFunction() {
  try {
    let resultAry = await Promise.all([
      TimeoutFunction(1, 3000),
      TimeoutFunction(2, 4000)
    ]);
    console.log('sum = ' + (resultAry[0] + resultAry[1]));
  } catch(e) {
    console.log(e);
  }
})();

function TimeoutFunction(count, waitTime) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('LongTimeProcess' + count);

      if (count === 1) {
        reject('エラーが発生しました');
      } else {
        resolve(count);
      }
    }, waitTime);
  });
}

Promiseを単独で使用するより、async/awaitと共に用いたほうが処理を簡潔に記述できそうですね。
本記事は以上となります。
お疲れさまでした。

次回はnode v8で追加されたutil.promisify()を予定しています。

それではまた、次の記事にて!

コメント

タイトルとURLをコピーしました