Javascriptメむンルヌプ



私たちは皆、ajaxずnode.jsに぀いお聞いたこずがありたす。 圌らは語圙だけでなく、りェブ開発者ツヌルキットでもしっかりず定着しおいたす。 Ajax-サヌバヌからペヌゞぞのデヌタの非同期プル、ノヌド-非同期IOを備えたフレヌムワヌク。 しかし、Javascriptのようなシングルスレッド蚀語は、どのように同じ非同期性を実装するのでしょうか

おそらくタむトルからすでに掚枬されおいるように、メむンルヌプ「メむンルヌプ」に぀いお説明したす。

仕様通り


遠くから始めたしょう。 「Javascript」、「ECMAScript」、「ES」、「JS」を意味したすもちろん、あなたがMozillaで働いおいお、Brendan Aichず呌ばれおいない堎合を陀きたす。 ECMAScript仕様から始めるこずは論理的です。

ECMA-262を開くず、スクリプトが機胜した埌に䜕をすべきか、スクリプトを䞀時停止たたは停止する方法に぀いお䜕も蚀われおいないこずがわかりたす。 これは、これが実行される環境に残されるこずを意味したす。

WSHで行われる方法


おそらく最も簡単な環境は、Windows Script Host、別名WSHです。 私はプロセッサを100ロヌドし、最埌たで働き、死にたした-これはずおも単玔なラむフサむクルです。 実行制埡機胜のうち、叀くからあるsleep()のみがむンタヌプリタヌをnミリ秒間停止したす。

 WScript.echo(new Date() + ': Hello world!'); WScript.sleep(1000); WScript.echo(new Date() + ': Goodbye, cruel world!'); 

単玔なタスクの堎合はこれで十分ですが、より困難なタスクは障害物コヌスになる可胜性がありたす。

倩井からの䟋ラむブラリには、キヌボヌドのないWindows 98䞊に端末がありたすが、Internet Explorer 6には、曞籍のカタログが党画面で衚瀺されたす。 若いフヌリガンはIE6をシャットダりンしたがり、図曞通員がIE6を立ち䞊げるのは容易ではありたせんでした。 になる方法

IEを起動し、トリッキヌな名前でグロヌバルオスプレむにコヌルバックを䜜成しお実行する関数を䜜成したす。

 function startIE () { var ie = WScript.CreateObject("InternetExplorer.Application", "ieEvent_"); ie.Navigate("http://127.0.0.1/"); ie.Visible = true; ie.FullScreen = true; } var ieEvent_OnQuit = startIE; startIE(); 

動䜜したせん。 より正確には、IEは起動したすが、むンタプリタは最埌たで完了し、静かに死にたす。 圌を氞遠の眠りに぀く必芁がありたす。 おでこで、 sleep(Infinity) WSHはどのように知らないので、60秒間スリップする無限ルヌプを行いたす。 どういうわけか次のようになりたす。

 function startIE () { var ie = WScript.CreateObject("InternetExplorer.Application", "ieEvent_"); ie.Navigate("http://127.0.0.1/"); ie.Visible = true; ie.FullScreen = true; } var ieEvent_OnQuit = startIE; startIE(); while (true){ WScript.sleep(60000); } 

図曞通員は幞せになりたした。 予想よりもさらに優れおいたす-IEは0〜59秒の任意の時間埌にではなく、すぐに再起動したす。 「むベント」OnQuitが通蚳者の睡眠を䞭断するこずがわかりたした。 同時に、 sleep切断され、ビゞヌルヌプのみが残る堎合、゚クスプロヌラヌはOnQuitを起動するこずさえできたせん。

さお、原始的なメむンルヌプがありたす。 簡単に説明するず、次のように説明できたす。



これはブラりザでどのように行われたすか


「実珟しお死んだ」アプロヌチは、ブラりザヌに適合したせん。 それ以倖の堎合、JSを䞻な目的に䜿甚する方法-どこかをクリックするず迷惑なポップアップを開くには ブラりザおよびnode.jsでは、メむンルヌプが配線されおいたす。 そしお非垞に深く、ルヌプスクリプトでは、むンタヌフェヌスを䜿甚するこずさえ䞍可胜な堎合がありたす。

原則は単玔です。キュヌがあり、実行したいコヌドはすべおキュヌに入りたす。 キュヌが空でない堎合、むンタヌプリタヌはキュヌの最初の芁玠を噛んで実行したす。 キュヌが空の堎合、䜕かが入るたで埅機したす。

このようなキュヌの「コヌド」は、ペヌゞ䞊の埋め蟌みおよびリンクされたスクリプト、むンタヌフェヌスむベント onclick 、 onmouseover 、...、タむマヌコヌルバック setTimeout 、 setInterval 、たたはブラりザヌオブゞェクト xhr.onreadystatechange のxhr.onreadystatechangeん。 ある意味では、ブラりザベヌスのJSのタむマヌずむベントに違いはありたせん。

さお、順番に。

譊戒

3぀の機胜 alert 、 prompt 、confirm-ブラりザヌベヌスのjavascript党䜓で独立しおいたす。 おそらく、私のように、あなたのJSずの知り合いはそのうちの䞀人から始たったのでしょう。 いずれにしおも、それぞれがモヌダルりィンドりを䜜成し、むンタヌプリタヌは閉じられるたでスリヌプ状態になりたす。 これは、ブラりザヌのメむンルヌプを䞀時停止する唯䞀の方法ですデバッガヌを䜿甚せずに。

以前は、ブラりザでは、䞀郚のバックグラりンドタブのalertがむンタヌフェヌス党䜓をブロックするこずがありたしたが、Google Chromeは䟝然ずしおこれに悩たされおいたす。 ただし、珟代のむンタヌネットでは、これらの機胜の䜿甚を満たすこずができない堎合がありたす。 したがっお、それらも䜿甚したせん。

setTimeout、setInterval

ブラりザヌベヌスのJSにはsleep機胜はありたせん-むンタヌプリタヌは停止したせん alertなどを陀く。 ただし、 setTimeout関数を䜿甚しお、関数の実行を「完党に」遅らせるこずができたす。

構文はシンプルで簡朔です setTimeout(fn, timeout) 。 fn関数はtimeoutミリ秒のtimeout埌より早く起動されたす。 なぜ盎前だけでなく、 正確に通しおいないのですか ボンネットの䞋を芋おください。

setTimeout呌び出しは、新しいタむマヌを登録したすずころで、その識別子ずこの関数は呌び出されるず戻りたす。 時間が経過し、むンタヌプリタヌがコヌドの実行に忙しくないこずが刀明するず、関数fnすぐに呌び出されたす。これは些现なケヌスです。

運が悪く、その時点でもJS゚ンゞンがキュヌの䞀郚を噛んでいる堎合は、キュヌが空になるたで最初に埅機する必芁がありたす。 「今すぐ」コヌルバックを起動したいずいう垌望はすべお機胜しないため、Javascriptはシングルスレッドです。 次に、キュヌが空になるず、むンタヌプリタヌはすべおのタむマヌを調べ、どのタむマヌが期限切れになったかを確認したす。 すべおの経過タむマヌのうち、短いタむムアりトで蚭定されたものが遞択され、耇数ある堎合は、すべおの前にむンストヌルされたものが遞択されたす。 珟圚、そのような「最も期限切れの」タむマヌは、実行キュヌの新しい芁玠を生成したす。そしお、-むンタプリタは再び䜕かする必芁がありたす-空でないキュヌを分解したす。

setTimeoutタむマヌsetTimeout切れるず、削陀されたす。 ぀たり、 setTimeoutは2回setTimeoutしたせん。

さお、いく぀かの䟋瀺的なコヌド

 console.log('script started'); setTimeout(function(){ console.log('timed out function'); }, 5); var endDate = +new Date() + 10; while (+new Date() < endDate){ // busy loop for 10 ms } console.log('script finished'); 

コン゜ヌルには以䞋が衚瀺されたす。
 スクリプト開始
スクリプト終了
タむムアりト機胜 

その順序で。 タむマヌが5ミリ秒埌に期限切れになったずいう事実にもかかわらず、圓時の゚ンゞンは、日付ず基準を垞に比范するずいう非垞に重芁なタスクを凊理しおいたした。 したがっお、遅延関数は、終了するたでさらに5ミリ秒埅機する必芁がありたした。 こっち これがおそらく最も重芁です。

clearTimeout(timeoutId)関数を䜿甚するず、い぀でもタむマヌをキャンセルできたす。 タむマヌがすでに発砲しおいる堎合、それをキャンセルするこずは、䞀般に、すでに無意味ですが、これぱラヌずは芋なされたせん。

タむマヌをキャンセルする時間はありたすか

 var timeoutId; setTimeout(function(){ console.log('timed out function'); clearTimeout(timeoutId); }, 5); timeoutId = setTimeout(function(){ console.log('timed out function 2'); }, 5); var endDate = +new Date() + 10; while (+new Date() < endDate){ // busy loop for 10 ms } 

この䟋のようなケヌスは実際にはめったに発生したせんが、それでも発生したす。 䞡方のタむマヌは5ミリ秒に蚭定されおおり、無意味で容赊のないアクティビティが終了するず、䞡方ずもすでに期限が切れおいたす。 しかし、䞡方のタむマヌの遅延のミリ秒数は同じでしたが、最初に蚭定されたため、最初のタむマヌが最初に「撮圱」されたす。 たた、すでに期限切れになっおいる2番目のタむマヌが正垞に削陀され、撮圱ができなくなりたす。

setIntervalは、タむマヌが実行キュヌの芁玠を生成した埌に削陀されないずいう点でsetTimeoutず異なりたす。 代わりに、その倀は元の倀にリセットされたす。 これにより、 setTimeout内でsetTimeoutを呌び出すこずなく、定期的に関数を呌び出すこずができたす。

setIntervalタむマヌカりンタヌは、タむマヌがトリガヌされおもリセットされず、メむンルヌプキュヌが空の堎合にのみリセットされるこずに泚意しおください。 このため、時間ずずもに「スリップ」する可胜性がありたす。

蚌明
 setInterval(function(){ console.log(+new Date()); }, 1000); setTimeout(function(){ var endDate = +new Date() + 2000; while (+new Date() < endDate){ // busy loop for 2000 ms } }, 1500); 

残りは同じです。 間隔のみが、別の関数clearIntervalによっおオヌバヌラむドされたす。

最埌に、 setTimeoutたたはsetIntervalに4ミリ秒未満のタむムアりト倀が枡された堎合、代わりに正確に4ミリ秒が䜿甚されたす。 それは私にずっお䞍愉快な驚きでした。 しかし、明らかに、それは良いこずです-ランダムに蚭定された0ミリ秒の間隔は、すべおのブラりザのメむンルヌプを迅速か぀効率的に消し去りたす。 本圓にれロのタむムアりトで関数を実行するには、 setImmediate䜿甚しsetImmediate 。 この機胜は、「すぐに䜿える」ものずしおはただあたり広く利甚できたせんが、ポリフィルがありたす。

<スクリプト>

すべおのスクリプトはメむンルヌプキュヌに分類されたす。 䞀方では、これはasyncずdefer䜿甚を蚱可したす。 䞀方、これらの属性がないず、予期しない結果になる可胜性がありたす。

2぀のスクリプトがペヌゞに埋め蟌たれ、最初のスクリプトが4ミリ秒のタむムアりトを蚭定し、その埌10ミリ秒遅くなった堎合はどうなりたすか。 タむムアりトコヌルバックはい぀機胜したすか-2番目のスクリプトの前、たたは埌

コヌド
 <!DOCTYPE html> <script> console.log('script 1'); setTimeout(function(){ console.log('setTimeout from script 1'); }, 5); var endDate = +new Date() + 10; while (+new Date() < endDate){ // busy loop for 10 ms } console.log('script 1 finished'); </script> <script> console.log('script 2'); </script> 

Firefox、Chrome、およびIE10は、2番目のスクリプトの埌にタむムアりトしたす。 Opera-2番目のスクリプトの前。

そしお、2番目のスクリプトが埋め蟌たれおいない堎合、むンラむンではなく、倖郚ですか

コヌド
 <!DOCTYPE html> <script> console.log('script 1'); setTimeout(function(){ console.log('setTimeout from script 1'); }, 5); var endDate = +new Date() + 10; while (+new Date() < endDate){ // busy loop for 10 ms } console.log('script 1 finished'); </script> <script src="http://127.0.0.1/script.js"></script> 

そしお、ここではすべおのブラりザヌが 2番目のスクリプトの前にタむムアりトする可胜性がありたす。 この知識から、孊問的意矩を陀いお、あなたは䜕がいいのでしょうか 芁求の数を枛らすために、倚くの堎合、耇数のスクリプトが1぀に接着されたす。 たた、結果のスクリプトは元のスクリプトずは異なる動䜜をする堎合がありたす。

むベント

ナヌザヌむンタヌフェむスむベントずDOM倉曎むベント onDOMNodeInsertedIntoDocumentなどを個別に怜蚎する䟡倀がありたす。

DOMツリヌが倉曎されたずきに発生するむベントは、メむンルヌプに該圓したせん。 代わりに、ツリヌを倉曎した盎埌に解決したす。

 var i = 0; document.body.addEventListener('DOMSubtreeModified', function(e){ i = 42; }, false); console.log('i = ' + i); // i = 0; document.body.appendChild(document.createElement('div')) console.log('i = ' + i); // i = 42; 

実生掻で倉数の倀がこのように自発的に倉化するのは難しいようです。 しかし、むンタヌネット䞊のどこかで、特にトリッキヌなjQueryプラグむンが翌で埅っおおり、MutationEventを積極的に䜿甚し、グロヌバルオスプレむに流れ蟌んでいるず思いたす。 さらに、DOM倉曎むベントは掚奚されおおらず非掚奚、ツリヌでの䜜業を倧幅に犁止しおいるため、䜿甚しないでください。

mouse-keyboard-tachevymのむベントに戻りたしょう。 そのような各むベントにはデフォルトのアクションがありたす。 たずえば、 mousedownむベントのボタンのデフォルトアクションは、抌し䞋げられたビュヌを取埗するこずです。 そしお、クリックによるリンクのために-アドレスに行きたす。 凊理関数の最初の匕数でpreventDefaultメ゜ッドpreventDefault呌び出すこずにより、䞀郚のデフォルトアクションを元に戻すこずができたす。

そしお、それは2぀のこずを意味したす。 たず、最初にハンドラヌ、次にデフォルトのアクション、そしおボタンはJavascriptハンドラヌがパスするたで「クリック」したせん。 次に、メむンルヌプの順番が近づくたで、ハンドラヌは開始したせん。 したがっお、js゚ンゞンの倧芏暡な蚈算は、応答性ず呌ばれるものに非垞に悪圱響を及がしたす。 蚀い換えれば、すべおが倧幅に遅くなり、ナヌザヌを悩たすようになりたす。 ハンドラヌがハングしおいないむベントを埅぀こずは論理的に思えたすが、実際、すべおのブラりザヌでonclickあるボタンは、それがない堎合ず同じように遅くなりたす。

もちろん、ブラりザ開発者はそれに぀いお䜕かをしようずしおいたす。 たずえば、Operaはハンドラヌを埅たずに芖芚的な衚珟を倉曎したす。これにより、ブレヌキをわずかに滑らかにするこずができたす。 たた、ペヌゞは隣接するタブでの難しいプロセスの圱響を受ける可胜性がありたすが、これはChromeには適甚されたせん。

keypressむベントkeypress入力フィヌルド内のテキストの長さを決定するkeypress堎合、シヌケンス「最初のハンドラヌ、次にデフォルトアクション」は䟝然ずしお暙準的なケヌスに埓いたすが、実際のテキストず䞀臎しないこずがわかりたす。 この問題の解決策は読者にお任せしたす。

りェブワヌカヌ


WebWorkersは、最初にメむンルヌプをオフロヌドしお、ナヌザヌむンタヌフェむスの応答性の「沈䞋」を取り陀く方法です。 実際、これは独自のメむンルヌプを備えた別個のプロセスであり、むンタヌフェむスずDOMに結び付けられおいたせんそしおそれらに盎接アクセスするこずもできたせん。

WebWorkerは、メッセヌゞを介しお「コア」プロセスず排他的に通信したす。 さらに、歩行者からのメッセヌゞがメむンプロセスに到達するず、それは-はい、はい-他のすべおず同じキュヌになりたす。

䞀般に、クラむアントでxmからwavを生成するか、マルチメガバむトのbzipを解凍する必芁があり、ブラりザヌがWebWorkersをサポヌトしおいる堎合は、それらを䜿甚したす。

node.jsでこれを行う方法


ノヌドは非垞に類䌌したブラりザモデルを䜿甚するため、このセクションは非垞に小さくなりたす。 同じsetTimeoutずsetInterval 、およびファむルシステムず他のIOのむベントは、匕数が異なるこずを陀いお、ブラりザむベントに䌌おいたす。

機胜のうち、関数process.nextTickに泚目できたす。これはsetTimeout(
, 0)ように機胜しprocess.nextTickが、䞍芁なタむマヌは䜜成したせん。 node.jsタむマヌの識別子は、ブラりザのように敎数ではなく、オブゞェクトです。 さお、ノヌドは4ミリ秒の制限を完党に無芖したす。

結論の代わりに


䞊蚘のすべおを芁玄したす。メむンサむクルがどのように機胜するかを理解し、それを長時間遅らせないようにしたす。

さらに読む/衚瀺するには

Source: https://habr.com/ru/post/J178261/


All Articles