ホームページ > 記事 > ウェブフロントエンド > Nodejs のイベントとイベント ループを理解する
関連する推奨事項: 「nodejs チュートリアル 」
JavaScript に精通している友人は、マウスの移動、マウスのクリック、キーボード入力など。 JavaScript でこれらのイベントをリッスンして、対応する処理をトリガーします。
nodejs にもイベントがあり、特殊な処理を行うための特別なイベント モジュールもあります。
同時イベントとイベント ループも、nodejs で非同期 IO を構築するための非常に重要な概念です。
今日はそれについて詳しく学びます。
nodejs は、イベント用の特別なモジュール lib/events.js を提供します。
nodejs を使用して Web サーバーを構築することについて話していたときのことを覚えていますか?
const server = http.createServer((req, res) => { res.statusCode = 200 res.setHeader('Content-Type', 'text/plain') res.end('welcome to www.flydean.com\n') })
ここでは、各リクエストがリクエスト イベントをトリガーします。
nodejs のコア API は非同期イベント駆動型アーキテクチャに基づいているため、nodejs には多くのイベントがあります。
例: net.Server は新しい接続が確立されるたびにイベントをトリガーし、fs.ReadStream はファイルが開かれたときにイベントをトリガーし、stream はデータが読み取り可能になったときにイベントをトリガーします。
nodejs イベントを構築する方法を見てみましょう:
const EventEmitter = require('events') const eventEmitter = new EventEmitter()
イベントには一般的に使用される 2 つのメソッド、つまり on と Emit があります。
on はイベントをリッスンするために使用され、emit はイベントをトリガーするために使用されます。
eventEmitter.on('fire', () => { console.log('开火') }) eventEmitter.emit('fire')
emit はパラメータを取ることもできます。次のパラメータを見てみましょう:
eventEmitter.on('fire', who => { console.log(`开火 ${who}`) }) eventEmitter.emit('fire', '美帝')
2 つのパラメータを見てみましょう:
eventEmitter.on('fire', (who, when) => { console.log(`开火 ${who} ${when}`) }) eventEmitter.emit('fire', '川建国','now')
デフォルトでは、EventEmitter は次で始まります。登録された順序で同期的に呼び出されます。これにより、イベントの正しい順序が保証され、競合状態やロジック エラーを回避できます。
非同期実行が必要な場合は、setImmediate() または process.nextTick() を使用して非同期実行モードに切り替えることができます。
eventEmitter.on('fire', (who, when) => { setImmediate(() => { console.log(`开火 ${who} ${when}`); }); }) eventEmitter.emit('fire', '川建国','now')
さらに、イベントは他のいくつかのメソッドもサポートしています:
once(): 単一のリスナーを追加します
removeListener() / off(): From イベント リスナーを削除しますevents
removeAllListeners(): すべてのイベント リスナーを削除します
nodejs コードがシングルスレッド環境で実行されることはわかっています。はい、処理されるのは 1 つだけです。一度に。
この処理方法により、マルチスレッド環境におけるデータ同期の問題が回避され、処理効率が大幅に向上します。
いわゆるイベント ループとは、プログラム サイクルにおいて、プロセッサがこのサイクルのイベントを処理した後、次のイベント サイクルに入り、次のイベント サイクルのイベントを処理することを意味します。サイクルごとに。
イベント処理中にイベントの処理がブロックされると、他のイベントの実行に影響を与えるため、JS ではほぼすべての IO が非処理であることがわかります。 -ブロッキング。これが、JavaScript に非常に多くのコールバックが存在する理由でもあります。
簡単なイベント ループの例を見てみましょう:
const action2 = () => console.log('action2') const action3 = () => console.log('action3') const action1 = () => { console.log('action1') action2() action3() } action1()
上記のコード出力:
action1 action2 action3
関数間の呼び出しはスタックを通じて実装されることがわかっています。上記の例では、呼び出しシーケンスもスタックを通じて実装されています。
ただし、関数内のすべてのメソッドがスタックにプッシュされるわけではなく、一部のメソッドはメッセージ キューに入れられます。
別の例を挙げてみましょう:
const action2 = () => console.log('action2') const action3 = () => console.log('action3') const action1 = () => { console.log('action1') setTimeout(action2, 0) action3() } action1()
上記のコードを実行した結果は次のとおりです:
action1 action3 action2
結果は異なります。これは、settimeout がタイマーをトリガーするためで、タイマーが期限切れになると、コールバック関数はスタックに置かれるのではなく、処理されるメッセージ キューに置かれます。
イベント ループはスタック内のイベントに優先順位を付けます。スタックにデータがない場合にのみ、メッセージ キュー内のイベントの消費に切り替わります。
上記の例の setTimeout のタイムアウト時間は 0 ですが、action3 を実行するには、action3 が実行されるまで待つ必要があります。
setTimeout のタイムアウトは現在のスレッドで待機するのではなく、ブラウザまたは他の JS 実行環境によって呼び出されることに注意してください。
ES6 のプロミスでは、ジョブ キューの概念が導入されています。ジョブ キューを使用すると、非同期関数の結果を最後に配置するのではなく、できるだけ早く実行します。呼び出しスタック。
例:
const action2 = () => console.log('action2') const action3 = () => console.log('action3') const action1 = () => { console.log('action1') setTimeout(action2, 0) new Promise((resolve, reject) => resolve('应该在action3之后、action2之前') ).then(resolve => console.log(resolve)) action3() } action1()
出力結果:
action1 action3 应该在action3之后、action2之前 action2
これは、現在の関数の終了前に解決された Promise が現在の関数の直後に実行されるためです。
つまり、最初にスタックが実行され、次にジョブ キューが実行され、最後にメッセージ キューが実行されます。
まず、tick という定義を説明します。tick はイベント サイクルを指します。 Process.nextTick() は、次のイベント ループ ティックが開始する前にこの関数を呼び出すことを指します。
process.nextTick(() => { console.log('i am the next tick'); })
したがって、nextTick はメッセージ キューの setTimeout よりも高速である必要があります。
nodejs は、コードをできるだけ早く実行するための setImmediate メソッドを提供します。
setImmediate(() => { console.log('I am immediate!'); })
setImmediate の関数は、イベント ループの次の反復で実行されます。
setImmediate() と setTimeout(() => {}, 0) の関数は基本的に似ています。これらはすべて、イベント ループの次の反復で実行されます。
特定のコールバック関数を定期的に実行したい場合は、setInterval を使用する必要があります。
setInterval(() => { console.log('每隔2秒执行一次'); }, 2000)
要清除上面的定时任务,可以使用clearInterval:
const id = setInterval(() => { console.log('每隔2秒执行一次'); }, 2000) clearInterval(id)
注意,setInterval是每隔n毫秒启动一个函数,不管该函数是否执行完毕。
如果一个函数执行时间太长,就会导致下一个函数同时执行的情况,怎么解决这个问题呢?
我们可以考虑在回调函数内部再次调用setTimeout,这样形成递归的setTimeout调用:
const myFunction = () => { console.log('做完后,隔2s再次执行!'); setTimeout(myFunction, 2000) } setTimeout(myFunction, 2000)
更多编程相关知识,请访问:编程视频!!
以上がNodejs のイベントとイベント ループを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。