むベントルヌプの仕組み ― JavaScriptが「1人で䜕圹もこなす」秘密


むベントルヌプの仕組み コヌルスタック main() fetchData() handleClick() ↑ 䞊から順に実行 むベントルヌプ 1. スタック空 2. Micro党消化 3. Macro1぀取埗 4. 繰り返し マむクロタスクキュヌ ➤ 優先床: 高 Promise async/await コヌルバックキュヌ ➤ 優先床: 通垞 setTimeout onClick 実行 Node.js むベントルヌプのフェヌズlibuv timers I/O callbacks idle poll check close
むベントルヌプの仕組み ― コヌルスタック・キュヌ・Node.jsフェヌズの関係
ひよこ ひよこ

JavaScriptは「シングルスレッド」っお聞いたんだけど、それなのにどうしお色んな凊理を同時にこなせるの

ペンギン先生 ペンギン先生

いい質問だね。JavaScriptの゚ンゞンは確かに1本の糞スレッドしか持っおいないんだけど、「むベントルヌプ」ずいう仕組みのおかげで、たるで同時に動いおいるように芋えるんだよ。レストランで1人のりェむタヌが泚文を受けお、料理ができたテヌブルから順に配膳しおいくむメヌゞだね。

ひよこ ひよこ

むベントルヌプっお具䜓的にはどういう仕組みなの

ペンギン先生 ペンギン先生

倧きく3぀の登堎人物がいるよ。たず「コヌルスタック」は今たさに実行䞭の凊理を積み䞊げる堎所。次に「コヌルバックキュヌ」はsetTimeoutやクリックむベントなど、あずで実行したい凊理が䞊ぶ埅ち行列。そしお「マむクロタスクキュヌ」はPromiseのthenやasync/awaitの続きが入る優先キュヌだよ。むベントルヌプはコヌルスタックが空になるたびに、たずマむクロタスクキュヌを党郚凊理しお、そのあずコヌルバックキュヌから1぀取り出す、ずいうサむクルを繰り返しおいるんだ。

ひよこ ひよこ

マむクロタスクのほうが先に凊理されるんだねじゃあsetTimeout(fn, 0)ずPromise.resolve().then(fn)だず、どっちが先に動くの

ペンギン先生 ペンギン先生

Promise.resolve().then(fn)のほうが先だよ。setTimeoutのコヌルバックはコヌルバックキュヌマクロタスクキュヌに入るけど、Promiseの.thenはマむクロタスクキュヌに入る。むベントルヌプは必ずマむクロタスクを先に党郚消化するから、setTimeout(fn, 0)で「0ミリ秒埌」ず指定しおも、Promiseの埌回しになるんだ。

ひよこ ひよこ

なるほどブラりザずNode.jsだず、むベントルヌプっお違うの

ペンギン先生 ペンギン先生

基本の考え方は同じだけど、Node.jsのほうがもう少し现かいフェヌズに分かれおいるよ。Node.jsではlibuvずいうラむブラリがむベントルヌプを動かしおいお、timerssetTimeout/setInterval→ I/Oコヌルバック → idle/prepare → poll新しいI/Oむベントの取埗→ checksetImmediate→ closeコヌルバック、ずいう6぀のフェヌズを順番に回っおいるんだ。ブラりザのほうは画面描画レンダリングのタむミングもルヌプに組み蟌たれおいる点が倧きな違いだね。

ひよこ ひよこ

Node.jsのlibuvっお䜕をしおいるのJavaScriptはシングルスレッドなのに、ファむル読み曞きずかどうしおるの

ペンギン先生 ペンギン先生

そこがポむントだね。JavaScript自䜓はシングルスレッドだけど、libuvは内郚にスレッドプヌルを持っおいるんだ。ファむルの読み曞きやDNS解決みたいな、どうしおも時間がかかるブロッキング凊理は裏偎のスレッドプヌルに任せお、終わったらコヌルバックキュヌに結果を返す。だからJavaScriptのメむンスレッドは止たらずに次の凊理に進めるんだよ。

ひよこ ひよこ

async/awaitを䜿えば䞊列凊理ができるっお思っおたんだけど、違うの

ペンギン先生 ペンギン先生

これはよくある誀解だね。async/awaitはあくたで「非同期凊理を同期的な芋た目で曞ける構文糖」であっお、䞊列実行を実珟するものじゃないんだ。awaitするずその関数の実行は䞀時停止するけど、他のスレッドで動くわけじゃない。本圓に耇数のAPIを同時に呌びたいならPromise.allを䜿っお耇数のPromiseを同時に開始する必芁があるよ。それでもCPUの蚈算凊理を䞊列化するわけじゃなくお、I/O埅ちを重ね合わせおいるだけなんだ。

ひよこ ひよこ

ぞぇヌじゃあむベントルヌプで困るこずっおあるの

ペンギン先生 ペンギン先生

あるよ。ベテラン゚ンゞニアでも匕っかかる萜ずし穎が「マむクロタスクの飢逓starvation」だね。たずえばPromiseの.thenの䞭でたた新しいPromiseを解決しお.thenを登録する、ずいうのを繰り返すず、マむクロタスクキュヌが氞遠に空にならない。むベントルヌプはマむクロタスクを党郚消化するたでコヌルバックキュヌに進めないから、setTimeoutや画面描画がい぀たでも実行されず、ペヌゞがフリヌズしおしたうんだ。

ひよこ ひよこ

マむクロタスクが終わらないず次に進めないっお怖いね 察策はあるの

ペンギン先生 ペンギン先生

再垰的にマむクロタスクを生成するパタヌンを避けるのが基本だね。倧量のデヌタを凊理するなら、途䞭でsetTimeoutやrequestAnimationFrameを挟んでマクロタスクに逃がすテクニックがあるよ。Node.jsならsetImmediateでcheckフェヌズに回すのも有効だね。むベントルヌプの仕組みを理解しおいるず、パフォヌマンスチュヌニングやバグの原因特定が栌段に楜になるから、ぜひ芚えおおいおほしいポむントだよ。