おことわり
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 関数的なもの」と思ってもらえれば差し支えないです。
このとき、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);
});
おっと、async
でない普通の関数 (同期関数) と違い、返り値そのままでなく Promise
というオブジェクトが得られました。
そう、非同期関数は、先ほど書いた通り完了を待たずに並列に走るもの (仕組み) です。なので、関数を実行した瞬間では、まだ返り値 (結果) が確定していません (上サンプルでは固定値ですが)。そこで代わりに得られたのが 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);
});
返り値が得られました。何が起こったのでしょう?
僕の中でのイメージはこうです。
async
関数が実行されたとき、Promise
オブジェクトが生成されました。この Promise
という箱の中で、async
関数で書いた処理が進められます。
Promise
を await
すると、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);
});
まず f_promise
に Promise
オブジェクトを格納しています。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
関数」という方が正確です。