JavaScript の async/await の動きを自分なりに図にしてみた (返り値を得られるまで)

## おことわり

JavaScript の非同期処理をようやく理解できた時の、僕の初心者目線をほぼそのまま綴っています。あくまでも**僕の体感的解釈の話**であり、実際の動作を説明したものではありません。正確さに欠ける表現があるかもしれません。

## async 関数を実行すると

何か返り値のある `async` 関数を実行したとします。

```js
async function any_f() {
  // any sub process
  return "any_value";
}

window.addEventListener("load", async function() {
  // any main process
  any_f();
});
```

:::note
`window.addEventListener("load", ...);` というのは、簡単に言うと「ドキュメントのロードが終わってからこの処理をする」という命令になります。

ここでは「C で言う main 関数的なもの」と思ってもらえれば差し支えないです。
:::

![async-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/61ea85d9-f7a4-4f7d-fbe6-bf898d6a6233.png)

このとき、main 部分と sub 部分が並列 (※) して進みます。

:::note warn
(※) ここで「並列に動作 = マルチスレッド」ではありません。ブラウザの JavaScript (ECMAScript) は、通常は一度に 1つのタスクしか実行しません (つまりマルチスレッドでない)。

一定の時間単位で、動作中の複数タスク (上図で言う main/sub) の処理を切り替えて進めているため、並列に動作しているように振る舞います。
:::

次に、async 関数の返り値を受け取ってみましょう。

```diff_javascript
async function any_f() {
  // any sub process
  return "any_value";
}

window.addEventListener("load", async function() {
  // any main process
- any_f();
+ let f_value = any_f();
+ console.log(f_value);
});
```

![result-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/9194e8a4-f472-fd94-e2bb-b84478bb085f.png)

おっと、`async` でない普通の関数 (同期関数) と違い、返り値そのままでなく `Promise` というオブジェクトが得られました。

![async-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/fc06fd76-47cb-cc79-b305-b0e7c9d77fd5.png)

そう、非同期関数は、先ほど書いた通り完了を待たずに並列に走るもの (仕組み) です。なので、関数を実行した瞬間では、まだ返り値 (結果) が確定していません (上サンプルでは固定値ですが)。そこで代わりに得られたのが `Promise` オブジェクトです。

さて、この `Promise` とは何者でしょうか。また、どうすれば返り値が得られるでしょうか。

## await の意味と作用

ここで、説明は後でするとして `await` という命令が登場します。

```diff_javascript
async function any_f() {
  // any sub process
  return "any_value";
}

window.addEventListener("load", async function() {
  // any main process
- let f_value = any_f();
+ let f_value = await any_f();
  console.log(f_value);
});
```

![result-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/8001ad22-d43c-b5c1-3ad5-3803d2d80017.png)

返り値が得られました。何が起こったのでしょう？

僕の中でのイメージはこうです。

![async-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/be3ff75b-55a6-b19b-3fb6-88ac37df0331.png)

`async` 関数が実行されたとき、`Promise` オブジェクトが生成されました。この `Promise` という箱の中で、`async` 関数で書いた処理が進められます。

`Promise` を `await` すると、`Promise` から結果 (返り値) を得ようと試みます。このとき、まだ結果が確定していなければ、確定するまで呼び出し元は処理を止めます。

これが `await` 命令の意味です。

## 何かしながら待つ

(2023/08/08 追記)

上の例では、普通の同期関数と変わりありません。非同期の特徴を活かす「何かしながら待つ」をやってみましょう。

:::note
非同期の特徴というより、「何かしながら待つ」ために作られた仕組みが非同期処理という方が正確。
:::

```js
async function any_f() {
  // any sub process
  return "any_value";
}

window.addEventListener("load", async function() {
  let f_promise = any_f();

  // any main process
  console.log("any of main");

  let f_value = await f_promise;
  console.log(f_value);
});
```

![async-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/558738e3-43c6-f94b-15ea-9b39d9d13626.png)

まず `f_promise` に `Promise` オブジェクトを格納しています。`Promise` オブジェクトが生成された時点から、中で処理は進められています。

その処理をさせている一方で、何らかの別の処理 `// any main process` を片付けます。

さて、あとは `f_promise` の完了を待って、値を取り出します。これで「何かしながら待つ」ができました。

## 付録 - async 関数と Promise

以下のコードは等価です。

```js
async function any_f() {
  // any sub process
  return "any_value";
}

let promise = any_f();
```

```js
function any_f() {
  return new Promise(resolve => {
    // any sub process
    resolve("any_value");
  });
}

let promise = any_f();
```

「`async` 関数から `Promise` オブジェクトが生成される」というより、「`Promise` をより簡潔に書けるようにしたのが `async` 関数」という方が正確です。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function