💨

async / await

2024/09/06に公開

async

関数の先頭につけることで非同期関数という特殊な関数を定義できる。
非同期関数のreturnが返す値は必ずPromiseインスタンスになる。
仮に非同期関数の戻り地がPromiseでない場合は暗黙的にPromiseでラップされた値が返る。
returnが定義されていない場合には、undefinedがラップされたPromiseインスタンスが返る。

//非同期関数の宣言
async function fn(){...};

await

Promiseインスタンスの前に記述することで、Promiseのステータスがfulfilledかrejectedになるまで後続のコードの実行を待機する。非同期関数内でしか使用できない。
また、Promise内のresolveの実引数の値を取り返す役割もある。
awaitで受けたPromise内でrejectが実行された場合は例外を発生させる。なのでPromiseのcatchメソッドで行っていた処理は、try...catch構文で処理する。

async function fn(){
   let resolvedValue = await prom;
};

async / awaitを用いたコードの例

太郎と次郎と三郎は3人でかけっこしている。以下のrun関数にそれぞれの名前(personName)を渡して実行するとランダムな時間(time)が経過した後に途中でコケる(reject)か、完走(resolve)するようになっています。このrun関数はPromiseインスタンスを返すようになっている。

問1)3人でバトンをつないでリレーをする。太郎からスタートし完走すれば、「personNameのタイムはtimeです。」と出力してから次の走者をスタートさせる。仮に誰かがコケた場合には「personNameがコケました。レースやり直し!」と出力する。
以下はrun関数。

function run(personName) {
  return new Promise((resolve, reject) => {
    const time = Math.floor(Math.random() * 11);

    setTimeout(() => {
      if (time % 4 === 0) {
        reject({ personName });
      } else {
        resolve({ personName, time });
      }
    }, time);
  });
}

以下は回答。コメントアウトされているものはPromiseチェーンのコード。

function run(personName) {
  return new Promise((resolve, reject) => {
    const time = Math.floor(Math.random() * 11);

    setTimeout(() => {
      if (time % 4 === 0) {
        reject({ personName });
      } else {
        resolve({ personName, time });
      }
    }, time);
  });
}

const printTime = ({ personName, time }) => console.log(`${personName}のタイムは${time}です。`);

// run("太郎")
//   .then((result) => {
//     printTime(result);
//     return run("次郎");
//   })
//   .then((result) => {
//     printTime(result);
//     return run("三郎");
//   })
//   .then((result) => {
//     printTime(result);
//   })
//   .catch(({ personName }) => {
//     console.error(`${personName}がこけました。レースやり直し!`);
//   });


  async function runs(){
   try{
      let result = await run("太郎");
      printTime(result);
      result = await run("次郎");
      printTime(result);
      result = await run("三郎");
      printTime(result);
   }catch({personName}){
      console.error(`${personName}がこけました。レースやり直し!`);
   }
  }

  runs();

出力の例

太郎のタイムは5です。
次郎のタイムは10です。
三郎のタイムは6です。

問2)3人で一斉にスタートし最初にゴールした人の名前とタイムを出力する。全員がコケた場合には「レースやり直し!」と出力する。
以下は回答。

function run(personName) {
  return new Promise((resolve, reject) => {
    const time = Math.floor(Math.random() * 11);

    setTimeout(() => {
      if (time % 4 === 0) {
        reject({ personName });
      } else {
        resolve({ personName, time });
      }
    }, time);
  });
}

// Promise.any([run("太郎"), run("次郎"), run("三郎")])
//    .then(result => {
//       console.log(`一番早くゴールしたのは${result.personName}で、タイムは${result.time}です。`);
//    })
//    .catch(() => {
//       console.error("レースやり直し");
//    })

async function promiseAny(){
  try{
    let result = await Promise.any([run("太郎"), run("次郎"), run("三郎")]);
    console.log(`一番早くゴールしたのは${result.personName}で、タイムは${result.time}です。`);
  }catch(error){
    console.error("レースやり直し");
  }
}

promiseAny();

出力の例。

一番早くゴールしたのは次郎で、タイムは3です。

問3)3人で一斉にスタートし全員がゴールした場合にはそれぞれの名前とタイムを出力する。一人でもコケた場合は「personNameがコケました。レースやり直し!」と出力する。

function run(personName) {
  return new Promise((resolve, reject) => {
    const time = Math.floor(Math.random() * 11);

    setTimeout(() => {
      if (time % 4 === 0) {
        reject({ personName });
      } else {
        resolve({ personName, time });
      }
    }, time);
  });
}

// Promise.all([run("太郎"), run("次郎"), run("三郎")])
//    .then(result => {
//       for(const {personName, time} of result) {
//          console.log(`${personName}のタイムは${time}です。`)
//       }
//    })
//    .catch(err => {
//       console.error(`${err.personName}がコケました。レースやり直し!`);
//    })

async function promiseAll() {
  try{
    let result = await Promise.all([run("太郎"), run("次郎"), run("三郎")]);
    for(const{personName, time} of result){
    console.log(`${personName}のタイムは${time}です。`);
  }
  }catch({personName}){
    console.error(`${personName}がコケました。レースやり直し!`);
  }
}

promiseAll();

出力の例。

太郎のタイムは9です。
次郎のタイムは1です。
三郎のタイムは10です。

問4)3人で一斉にスタートし、全員がゴールまたはコケたときにそれぞれがゴールしたかコケたかを出力する。

function run(personName) {
  return new Promise((resolve, reject) => {
    const time = Math.floor(Math.random() * 11);

    setTimeout(() => {
      if (time % 4 === 0) {
        reject({ personName });
      } else {
        resolve({ personName, time });
      }
    }, time);
  });
}

Promise.allSettled([run("太郎"), run("次郎"), run("三郎")])
  .then((results) => {
    for (const { status, value, reason } of results) {
        if (status === "fulfilled") {
            console.log(`${value.personName}はゴールしました。`);
        } else {
            console.error(`${reason.personName}はコケました。`);
        }
    }
});

出力の例。

太郎はゴールしました。
次郎はコケました。
三郎はゴールしました。

問5)3人で一斉にスタートし、誰かがゴールまたはコケたときにその名前を出力する。

function run(personName) {
  return new Promise((resolve, reject) => {
    const time = Math.floor(Math.random() * 11);

    setTimeout(() => {
      if (time % 4 === 0) {
        reject({ personName });
      } else {
        resolve({ personName, time });
      }
    }, time);
  });
}

Promise.race([run("太郎"), run("次郎"), run("三郎")])
.then(({ personName }) => {
    console.log(`${personName}がゴールしました。`);
}).catch(({ personName }) => {
    console.error(`${personName}がコケました。`);
});

出力の例。

次郎がコケました。

Discussion