5
4

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-08-05

おことわり

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

async 関数を実行すると

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

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

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

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

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

async-1.png

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

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

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

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

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

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

async-2.png

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

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

await の意味と作用

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

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

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

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

async-3.png

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

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

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

何かしながら待つ

(2023/08/08 追記)

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

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

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

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

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

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

付録 - async 関数と Promise

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

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

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

let promise = any_f();

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

5
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4