【javascript】async、awaitの正確な仕様を理解する

前提知識

Microtask queueMacrotask queueー非同期処理が格納されるキュー。setTimeoutはMacrotask、Promiseのコールバック関数はMicrotask queueである。

・処理の優先順位は、コールスタックに入れられた関数>Microtask queue>Macrotask queue。コールスタックが空になったとき、Microtask queueが呼ばれ、その両方が空になったとき、Macrotask queueが呼ばれる。

・要するに、大まかに言うと、処理の順番は、「非同期じゃないコード」→「Microtask queueに入れられた関数(キュー入れられた順)」→「Macrotask queueに入れられた関数(キューに入れられた順)」である。(途中で優先度の高い処理が追加されると前に戻る)

仕様① awaitが読み込まれたときの挙動

・awaitの行に差し掛かった時、awaitの右に書かれた処理を実行した後、async関数を中断し、async関数を「Microtask queue」に入れる。

以下はコード例。

async function hello()
{
    console.log("hello")
    var b = await 1
    console.log("hello"+b)
}

console.log("start")
hello()
console.log("end")

上を実行すると、start→hello→end→hello1の順でログが出力される。

仕様② async関数の返り値

・async関数の返り値Promiseオブジェクト。

・async内の処理が終わっていない場合は、stateが「Pending」であるPromiseを返す。

・async内の処理が終わっている場合は、stateが「Fulfilled」であるPromiseを返す。

returnは関数の返り値とはならない。返り値は常にPromiseオブジェクトで、その中にreturnの結果が保存される。

以下はコード例。

async function hello()
{
    var b = await 1
    console.log("resume!")
    return b
}

a = hello()
console.log(a)

window.setTimeout(() => {
    console.log(a)
}, 1000)

上を実行すると、stateが「Pending」であるPromiseオブジェクトが出力された後、「resume!」が出力され、1秒後にstateが「Fulfilled」であるPromiseオブジェクトが出力される。

仕様③ 実行順序に気を付ける

・実は、仕様②で書いたコードのsetTimeoutの秒数を0にしても、結果は同じです。

・何故なら、「Microtask queue」→「Macrotask queue」の順に実行されるからです。

【javascript】厄介なビット演算の仕様を完全に理解する

導入

かなり厄介なので、正確な動きをかいておきます。

前提知識

・javascriptでは、数の型はNumberのみ。整数も小数も関係なくNumber型。

・Number型は、倍精度浮動点小数点数型であり、IEEE 754という規格が使われている。

・C#でいえばdouble。

・Number型の整数の精度は53bit。つまり、-2**53+1以上2**53-1以下の整数は正確に表記できる。(例えば、2*53+1のbit表記は2**53と同じ。)

・int32のbit表記には、補数表現が使われている。

javascriptのビット演算

ビット演算を行うときのみ、int32として扱われる。(>>>を含む計算は、uint32として計算されます。)

ビット演算が終了すると、int32(またはuint32)から通常のNumber型に戻る

Number→intへの変換

①小数の場合は、近い整数に変換される。

②整数x を ((x % 2**32)+2**32) % 2**32に変換する。簡単に言えば、mod 2**32において等しい、0以上2**32-1の整数に変換する。

③その整数の2進数表記をint32とみなす。(演算が>>>の場合はuint32としてみなす。)

int32→Numberへの変換

・int32における整数nの値は、そのまま、Number型におけるnに変換される。

(bit表記は変わるが、数の世界では変化なし。)

整理

・>>>が含まれない場合、-2**31以上2**31-1以下の整数は、ビット演算の前と後では変化しない。

・-2**31-1以下の場合や2**31以上の整数の場合、値は変化する。

整数の精度に注意

-2**53以下の整数と2**53以上の整数を扱う場合、意図していない値になるので注意。

例えば、(2**53+1)^0は、上の変換通りに計算すると、変換後は1になりそうですが、実際は0になります。これは、2**53+1のbit表記が2**53である為です。

・-2*31^0は、-2**31 mod 2**32 = 2**31より2**31に変換され、これを2進数を表すと、100…00(32bit)になる。これをint32で解釈すると-2**31となるので、計算終了後はNumber型の-2**31になる。

・2**31^0は2進数で表すと、100…00(32bit)になる。これをint32で解釈すると、-2**31となるので、計算終了後はNumber型の-2**31になる。

・-2**31^0は、-2**31-1 mod 2**32=2**31-1より2**31-1に変換され、これを2進数で表すと、01111…1111(32bit)になる。これをint32で解釈すると、2**31-1となるので、計算終了後はNumber型の2**31-1になる。

・-1^0は、2**32-1に変換され、これを2進数で表すと、111…111(32bit)になる。これをint32で解釈すると、-1となるので、計算終了後はNumber型の-1になる。

・(2**31^0) + (2**31^0)は、-2**32になる。

参考文献

↓ かなり分かりやすいjavascriptの数値型に関する記事です。

https://qiita.com/uhyo/items/f9abb94bcc0374d7ed23

【javascript】サイト上で音を鳴らす方法【2024年】

導入

2018年、Googleのポリシーが変更になり、javascriptでただAudioオブジェクトを作って、play()を呼ぶだけでは、音を鳴らすことができなくなりました。

様々なサイトで、すでに無効になった上記の方法が紹介されていて、2018年以降の方法を紹介しているサイトがあまりなかったので、ここに方法を書いておきます。

方法

①ユーザーにサイトをクリックをさせる。

②AudioContextオブジェクトのresume()を呼ぶ。

③その後に音を鳴らす。

(①と②は逆でも良いです。)

コード例

const atx = new AudioContext();
atx.resume();
const snd = new Audio("オーディオファイルへのパス.mp3");
document.addEventListener("click",() => {
     snd.currentTime = 0;
     snd.play();
})

上のコードは、クリックすると音が鳴るコードです。

currentTime = 0は、再生場所を設定しています。再生場所を元に戻さないと、連続再生しても、前の音が終わるまで次の音が鳴りません。

また、AudioContextのstateというプロパティで「音声が許可されたかどうか」が確認できます。stateが「running」の場合は許可あり、「suspended」の場合は許可なし、です。

ブラウザゲームを作る場合は、ミュートをデフォルトの設定するか、クリックするとゲームが始まるように設計すると良さそうです。

参考文献

https://developer.chrome.com/blog/autoplay