JavaScript
await

awaitを使う理由

awaitを使う理由は、別の処理に実行権を譲るためです。

JavaScriptはシングルスレッドであるため、実行権を持つ処理(以降「主処理」と呼びます)が自ら権利を手放さない限り、他の処理が実行されることはありません。主処理が10秒以上かかるような処理であっても同じです。ですので、その間は、主処理以外の処理によってコンテキスト(=DOMの状態やグローバル変数の値など)が書き換えられることはありません。

以下のコードを実行すると、スリープ前とスリープ後の出力は必ず同じになります。

const sleep = msec => {

const start = new Date().getTime();
while (new Date().getTime() - start < msec);
};

(() => {
console.log(window.scrollY); //=> 0
sleep(10000);
console.log(window.scrollY); //=> 0
})();

どれだけマウスやキーボードで操作しようとしても、出力内容は同じになります。そもそもJavaScriptはシングルスレッドであり、主処理が実行権を手放さない限り他の処理は実行されないため、スリープ中にマウスやキーボードでコンテキストを書き換えることはできません。


awaitを使う理由

重い処理をしている間もマウスやキーボードで操作できるようにしたい、複数の処理を並行して実行したいという場合、各処理が実行権を譲り合うよう実装されている必要があります。この「実行権を譲る実装」に使われるものがawaitです。

以下のコードを実行すると、スリープ中も画面をスクロールできます。

const sleep = async msec => {

return new Promise(resolve => {
window.setTimeout(() => resolve(), msec);
});
};

(async () => {
console.log(window.scrollY); //=> 0
await sleep(10000);
console.log(window.scrollY); //=> 1051
})();

スクロールした結果、スリープ前とスリープ後の出力内容が変わっています。このように、awaitを使うことで主処理が実行権を手放すことができ、「画面をスクロールする」という処理に実行権を移すことができます。


awaitの難しいところ

awaitにより他の処理に実行権が移ると、その処理によってコンテキストが書き換えられる可能性があります。先程の例が該当するのですが、スクロールすることでwindow.scrollYの値が書き換わっています。このように、awaitの後でコンテキストが書き換わっていることがあるため、await行以降の処理を実装する際は、コンテキストの書き換わりを考慮する必要があります。

console.log(window.scrollY);

await sleep(10000);
/*
* コンテキストの書き換わりを考慮した実装
*/

console.log(window.scrollY);

コンテキストの書き換わりを考慮する必要があるため、awaitを使わない実装に比べてプログラムが複雑になります。


まとめ

awaitを使う理由は「別の処理に実行権を譲るため」であり、awaitの難しいところは「コンテキストの書き換わりを考慮する必要があること」です。