async/await と Promise の解説 – JavaScript, Node.js

JavaScript(特にNode.js)で非同期処理は便利だけど、async/await、Promiseについて使い方がよくわからないという方に向けてご紹介します。

asyncの使い方

async とは非同期処理の関数宣言のことです。

async function example(arg){
  conset variable1 = 1;
  conset variable2 = 2;
  conset variable3 = 3;
}

宣言された関数の中の処理は全て非同期になります。通常のコード(同期)では、上から順番に処理されるため、variable1に1が代入された後にvariable2に2を代入する処理が行われます。しかし、非同期処理では、variable1に1の代入が完了する前に、variable2に2を代入する処理を走らせます。

非同期処理では、処理の完了を待たずに次の処理が実行されることが特徴です。

awaitの使い方

await とはasync関数の中でPromiseの結果が返されるまで待機する宣言のことです。

async function example(arg){
  const url = "https://xxx";
  const res = await fetch(url);
  console.log(res)
}

上記の例では、fetch処理が完了するまで待機し、fetchの結果をres変数に代入します。その後、async関数の続き(console.log)の実行を再開します。

await はasync関数の中でしか宣言できないので注意しましょう。

Promiseの使い方

Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を返します。

// Promiseを変数の処理として扱う場合
let width = 3;
let height = 5;
const rectangle_area = new Promise( (resolve, reject) => {
  try{
    resolve(width * height);
  }catch(error){
    reject(error)
  }
});
------
// Promiseを関数の処理として扱う場合
function rectangleArea(width, height){
  return new Promise( (resolve, reject) => {
    try{
      resolve(width * height);
    }catch(error){
      reject(error)
    }
  });
}

async () => {
  const rectangle_area = await rectangleArea(3, 5);
}

上記の例では、横幅(width)と縦幅(height)を掛け算した結果をrectangle_area変数に代入しています。

try catchで囲っているのは、非同期処理が正しく処理された場合と、エラーが発生した場合に切り分けるためです。(今回の例でエラーになることはないのですが、実際はAPI通信などを行うのでエラーハンドリングは必須処理です)

非同期処理が正常に動作した場合、resolveで返し、エラーが発生した場合rejectで返すという使い分けをします。

実践的な使い方 – 個別の非同期処理をまとめて行う

互いに依存関係のない個別の非同期処理を複数個まとめて行う場合を考えます。

今回の例では、四角形の面積と円の面積と三角形の面積を求め、その後全て整数になるよう四捨五入させます。

function rectangleArea(width, height){
  // 四角形の面積を求める処理
  return new Promise( (resolve, reject)=>{
    try{
      resolve(width*height);
    }catch(error){
      reject(error)
    }
  });
}
function triangleArea(width, height){
  // 三角形の面積を求める処理
  return new Promise( (resolve, reject)=>{
    try{
      resolve(width*height/2);
    }catch(error){
      reject(error)
    }
  });
}
function circleArea(radius){
  // 円の面積を求める処理
  return new Promise( (resolve, reject)=>{
    try{
      resolve(radius*radius*Math.PI);
    }catch(error){
      reject(error)
    }
  });
}

async function calculatedCircle(width, height, radius){
  try{
    const results = await Promise.all([
      rectangleArea(width, height),
      triangleArea(width, height),
      circleArea(radius)
    ]);
    const result_rounded = await Promise.all(results.map( result => Math.round(result)));
    return result_rounded;
  }catch(error){
    console.log(error)
  }
}

(async () => {
  const width = 3.2;
  const height = 5;
  const radius = 2;
  const data = await calculatedCircle(width, height, radius);
  console.log(data);
})();

上記の例では、Promiseを使った面積を求める関数を定義し、calculatedCircle関数の中で一斉に面積を求める計算をしています。求めた面積をmapメソッドを使って、四捨五入しています。

Promise.all は全ての非同期処理を同時に実行し、全ての処理が完了した後に値を返します。

これにより、一つ一つ計算するより早く処理が完了します。

実践的な使い方 – 連続した非同期処理を行う

一つの処理結果が次の処理に影響を与える場合について考えます。

今回の例では、正方形の中に円がある場合、下図の斜線部分の面積を求める問題を考えてみましょう。

半径の分からない円の面積|中学受験プロ講師ブログ
function rectangleArea(length){
  // 正方形の面積を求める処理
  return new Promise( (resolve, reject)=>{
    try{
      resolve(length*length);
    }catch(error){
      reject(error)
    }
  });
}
function circleArea(radius){
  // 円の面積を求める処理
  return new Promise( (resolve, reject)=>{
    try{
      resolve(radius*radius*Math.PI);
    }catch(error){
      reject(error)
    }
  });
}

function calcuratedArea(length){
  let answer;
  return rectangleArea(length)
    .then( rec => {
      answer = rec;
      return circleArea(length/2);
    }) 
    .then( cir => {
        answer = (answer - cir) / 4;
        return answer;
    })
}

calcuratedArea(5).then( ans => {
  console.log(ans);
});

ここでのポイントは、 then() で処理を連結しているところです。

正方形の面積の計算結果をthenの引数に渡し、return で次の円の面積を求める処理を宣言します。

次に、円の面積の計算結果を使って、最終的に求める面積を計算しています。

returnで返す処理も非同期処理なら続けてthen()で連結することができ、値を返せば最終的な返り値となります。

Promise.all の場合は、互いに独立した処理だったので同時に実行できましたが、一つの処理結果を次の処理に利用する場合には、thenで連結して処理を行います。

まとめ

覚えておこう!

並列した処理の場合:Promise.allを使おう!

連続した処理の場合:then()を使おう!

コメント

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