⛳️

async/awaitをなるべくシンプルに理解する

2023/07/06に公開

async/awaitの構文

const asyncFunc = async () => {
  const result = await Promise.resolve('Hello');
  console.log(result); // 'Hello'
}

asyncawaitそれぞれの役割

キーワード 役割
async 関数の前に付けることで、返り値がPromiseオブジェクトになる
await 処理を一時停止し、解決に伴って再開する。再開後、Promiseオブジェクトの解決された値を返す

awaitをただ付けるだけでPromiseをアンラップした値を返してくれるため、同期処理と比較しても大きなギャップなく実装できるのが特徴です。

const asyncFunc = async () => {
  const result1 = await asyncFunc1();
  const result2 = await asyncFunc2(result1);
  const result3 = await asyncFunc3(result1, result2);
}

async/awaitが導入される前は、Promiseにより非同期処理を行っていましたが、Promiseの場合、解決された値を取得するためにはthen()メソッドを用いる必要があり、かつ前回の解決された値を使い回したい場合は、then()をネストする必要があり、コールバックヘルのような問題を引き起こす恐れがありました。

asyncFunc1()
  .then((result1) => asyncFunc2(result1)
    .then((result2) => asyncFunc3(result1, result2))
  )

異常系
Promiseオブジェクトが拒否された場合、awaitはそれと同じエラーを投げます。

const asyncFunc = async () => {
  try {
    const result = await Promise.reject(new Error('エラー'));
  } catch (error) {
    console.log(error); // Error: エラー ...(省略)
  }
}

Promiseオブジェクトの生成

前述の通り、asyncを前につけた関数の返り値はPromiseオブジェクトであり、またawaitが待ち受けるものはPromiseオブジェクトです。
Promiseオブジェクトは、Promiseのコンストラクタを使って生成することができます。

// Promiseオブジェクトの解決後に、then()メソッドのコールバックが実行される
new Promise((resolve, reject) =>
  setTimeout(() => {
    resolve('Hello');
  }, 1000)
).then((result) => {
  console.log(result);
});

上記のコードを実行すると、まず処理中(pending)の状態をもつPromiseオブジェクトが同期的に返されます。
その後、Promiseオブジェクトが解決された(fulfilled)状態になると、解決後の値が返されます。
今回の場合、1秒後にコンソールに「Hello」と返されます。

Promise {
  <pending>,
  # ... (省略)
}
> Hello

より簡潔な手段として、Promise.resolve()Promise.reject()も用意されています。
これらのメソッドは同期的な値を引数に取り、解決されたPromise(または拒否されたPromise)を返します。
(前の項目では、こちらを使っていました)

// 解決されたPromiseを返す
Promise.resolve('Hello');

// 拒否されたPromiseを返す
Promise.reject(new Error('エラー'));

Promiseオブジェクトの持つ状態

Promiseオブジェクトは、処理状況に応じた状態を持ちます。
前の項目の、Promiseコンストラクタを使ったPromiseオブジェクトの生成の例から、状態の遷移を見ていきます。

// 再掲
new Promise((resolve, reject) =>
  setTimeout(() => {
    resolve('Hello');
  }, 1000)
).then((result) => {
  console.log(result);
});

上記のコードでは、1秒経過後にPromiseオブジェクトが解決されますが、解決されるまでの間、つまり処理中の状態は、pendingとなります。
そして、解決後はfulfilledという状態に遷移します。

解決されず、拒否された場合はどうでしょうか。

// Promiseオブジェクトが拒否され、catch()メソッドのコールバックが実行される
new Promise((resolve, reject) =>
  setTimeout(() => {
    reject(new Error('エラー'));
  }, 1000)
).catch((error) => {
  console.log(error);
});

上記の場合、Promiseオブジェクトは拒否され、エラーが投げられます。
拒否されるまでの処理中の際は、同様にpendingとなります。
そして、拒否された後、rejectedという状態に遷移します。

fulfilledやrejectedの状態を、まとめてsettledと呼びます。

冒頭のawaitの説明に、「処理を一時停止し、解決に伴って再開する」とありましたが、これはPromiseオブジェクトの状態がpendingなら一時停止、fulfilledなら再開される、ということです。
もし解決されずrejectedになったら例外がスローされ、try/catch文を使用してエラーハンドリングを行うことができます。

参考文献

この記事は以下の情報を参考にして執筆しました。

https://www.oreilly.co.jp/books/9784873119236/


オプティマインドでは「多様性が進んだ世の中でも、全ての人に物が届く世界を持続可能にする」という物流業界の壮大な社会課題を解決すべく、 一緒に働く仲間を大募集中です。 少しでも興味が湧いた方は是非お気軽にカジュアル面談をお申し込みください!

https://recruit.optimind.tech/

Discussion