イベント ループは Node.js の基本的な部分です。メイン スレッドがブロックされないようにすることで、非同期プログラミングが可能になります。イベント ループを理解することは、効率的なアプリケーションを構築するために重要です。次の記事では、Node のイベント ループについて詳しく説明します。お役に立てれば幸いです。
あなたはしばらく Node.js を使用し、いくつかのアプリを構築し、さまざまなモジュールを試し、非同期プログラミングにも慣れてきたと思います。しかし、何かがあなたを悩ませ続けます - イベントループです。
あなたも私と同じなら、イベント ループを理解するためにドキュメントを読んだり、ビデオを見たりするのに数え切れないほどの時間を費やしてきました。しかし、経験豊富な開発者であっても、その仕組みを完全に理解するのは難しいかもしれません。そのため、Node.js イベント ループを完全に理解できるように、このビジュアル ガイドを用意しました。それでは、座ってコーヒーを飲みながら、Node.js イベント ループの世界に飛び込んでみましょう。 [関連チュートリアルの推奨事項: nodejs ビデオ チュートリアル 、プログラミング教育 ]
から始めますJavaScript での非同期プログラミングのレビューが始まります。 JavaScript は Web、モバイル、デスクトップ アプリケーションで使用されますが、本質的には、JavaScript は同期、ブロッキング、シングルスレッド言語 であることを覚えておくことが重要です。短いコードスニペットを通してこの文を理解してみましょう。
// index.js function A() { console.log("A"); } function B() { console.log("B"); } A() B() // Logs A and then B
メッセージをコンソールに記録する 2 つの関数がある場合、コードは上から下に実行され、毎回 1 つだけ実行されます。ライン。上記のコード スニペットでは、A が B の前に記録されていることがわかります。
JavaScript は同期の性質によりブロックされています。前のプロセスにどれだけ時間がかかっても、前のプロセスが完了するまで後続のプロセスは開始されません。コード スニペットでは、関数 A が大きなコード ブロックを実行する必要がある場合、JavaScript は関数 B に分岐せずにその操作を完了する必要があります。たとえこのコードに 10 秒、あるいは 1 分かかっていたとしてもです。
お使いのブラウザでこのような状況に遭遇したことがあるかもしれません。 Web アプリケーションがブラウザーで実行され、ブラウザーに制御を返さずにコードの集中的なブロックを実行すると、ブラウザーがフリーズすることがあります。これをブロッキングと呼びます。 Web アプリケーションがプロセッサ制御をブラウザに戻すまで、ブラウザはユーザー入力の処理や他のタスクの実行を継続できなくなります。
スレッドとは、JavaScript プログラムがタスクを実行するために使用できるプロセスです。各スレッドは一度に 1 つのタスクのみを実行できます。マルチスレッドをサポートし、複数のタスクを同時に実行できる他の言語とは異なり、JavaScript にはコードを実行するメインスレッドと呼ばれるスレッドが 1 つだけあります。
ご想像のとおり、この JavaScript モデルでは、コードの実行を続行する前にデータがフェッチされるまで待たなければならないため、問題が発生します。この待機には数秒かかる場合があり、その間は他のコードを実行できません。 JavaScript が待機せずに処理を続行すると、エラーが発生します。 JavaScript で非同期動作を実装する必要があります。 Node.js にアクセスして見てみましょう。
Node.js ランタイムはブラウザを使わずに利用できる環境です 使用して実行しますJavaScript プログラム。コア - ノード ランタイムは、3 つの主要コンポーネントで構成されます。
すべての部分が重要ですが、Node.js の非同期プログラミングの主要なコンポーネントは libuv です。
Libuv は、C 言語で書かれたクロスプラットフォームのオープンソース ライブラリです。 Node.js ランタイムにおけるその役割は、非同期操作の処理のサポートを提供することです。仕組みを見てみましょう。
让我们来概括一下代码在 Node 运行时中的执行方式。在执行代码时,位于图片左侧的 V8 引擎负责 JavaScript 代码的执行。该引擎包含一个内存堆(Memory heap)和一个调用栈(Call stack)。
每当声明变量或函数时,都会在堆上分配内存。执行代码时,函数就会被推入调用栈中。当函数返回时,它就从调用栈中弹出了。这是对栈数据结构的简单实现,最后添加的项是第一个被移除。在图片右侧,是负责处理异步方法的 libuv。
每当我们执行异步方法时,libuv 接管任务的执行。然后使用操作系统本地异步机制运行任务。如果本地机制不可用或不足,则利用其线程池来运行任务,并确保主线程不被阻塞。
首先,让我们来看一下同步代码执行。以下代码由三个控制台日志语句组成,依次记录“First”,“Second”和“Third”。我们按照运行时执行顺序来查看代码。
// index.js console.log("First"); console.log("Second"); console.log("Third");
以下是 Node 运行时执行同步代码的可视化展示。
执行的主线程始终从全局作用域开始。全局函数(如果我们可以这样称呼它)被推入堆栈中。然后,在第 1 行,我们有一个控制台日志语句。这个函数被推入堆栈中。假设这个发生在 1 毫秒时,“First” 被记录在控制台上。然后,这个函数从堆栈中弹出。
执行到第 2 行时。假设到第 2 毫秒了,log 函数再次被推入堆栈中。“Second”被记录在控制台上,并弹出该函数。
最后,执行到第 3 行了。第 3 毫秒时,log 函数被推入堆栈,“Third”将记录在控制台上,并弹出该函数。此时已经没有代码要执行,全局也被弹出。
接下来,让我们看一下异步代码执行。有以下代码片段:包含三个日志语句,但这次第二个日志语句传递给了fs.readFile()
作为回调函数。
执行的主线程始终从全局作用域开始。全局函数被推入堆栈。然后执行到第 1 行,在第 1 毫秒时,“First”被记录在控制台中,并弹出该函数。然后执行移动到第 2 行,在第 2毫秒时,readFile
方法被推入堆栈。由于 readFile
是异步操作,因此它会转移(off-loaded)到 libuv。
JavaScript 从调用堆栈中弹出了 readFile
方法,因为就第 2 行的执行而言,它的工作已经完成了。在后台,libuv 开始在单独的线程上读取文件内容。在第 3 毫秒时,JavaScript 继续进行到第 5 行,将 log 函数推入堆栈,“Third”被记录到控制台中,并将该函数弹出堆栈。
大约在第 4 毫秒左右,假设文件读取任务已经完成,则相关回调函数现在会在调用栈上执行, 在回调函数内部遇到 log 函数。
log 函数推入到到调用栈,“Second”被记录到控制台并弹出 log 函数 。由于回调函数中没有更多要执行的语句,因此也被弹出 。没有更多代码可运行了 ,所以全局函数也从堆栈中删除 。
控制台输出“First”,“Third”,然后是“Second”。
很明显,libuv 用于处理 Node.js 中的异步操作。对于像处理网络请求这样的异步操作,libuv 依赖于操作系统原生机制。对于没有本地 OS 支持的异步读取文件的操作,libuv 则依赖其线程池以确保主线程不被阻塞。然而,这也引发了一些问题。
setTimeout
和 setInterval
这类延迟执行回调函数的方法又是何时执行回调函数呢?setTimeout
和 readFile
这类异步任务同时完成,Node 如何决定哪个回调函数先在调用栈上运行?其中一个会有更多的优先级吗?所有这些问题都可以通过理解 libuv 核心部分——事件循环来得到答案。
技術的に言えば、イベント ループは単なる C 言語プログラムです。しかし、Node.js では、同期コードと非同期コードの実行を調整するための設計パターンと考えることができます。
イベント ループは、Node.js アプリケーションが実行されている限り実行されるループです。各ループには 6 つの異なるキューがあり、各キューには最終的にコール スタックで実行する必要がある 1 つ以上のコールバック関数が含まれています。
setTimeout## と同じ番号が保存されます # コールバック
setInterval に関連する関数。
モジュールや
http モジュールなど、すべての非同期メソッドに関連するコールバック関数が含まれています。関連するメソッドが提供されます。
関数に関連するコールバック関数を保持するチェック キューです。
関数に関連付けられたコールバック関数が格納されます。
に関連付けられたコールバック関数が格納されます。
非同期タスクが libuv で完了すると、Node はいつコール スタックで関連するコールバック関数を実行しますか?
回答:コールバック関数は、コール スタックが空の場合にのみ実行されます。
ノードはコールバック関数を実行する前にコールスタックが空になるのを待ちますか?それとも通常の実行フローを中断してコールバック関数を実行しますか?
回答:コールバック関数を実行しても、通常の実行フローは中断されません。
コールバック関数の実行を遅延させる setTimeout や
setInterval などのメソッドは、いつコールバック関数を実行しますか?
回答: setTimeout
および setInterval
のすべてのコールバック関数の中で最初の優先順位が実行されます (マイクロタスク キューに関係なく)。
2 つの非同期タスク (setTimeout
と readFile
など) が同時に完了した場合、ノードはどのコールバック関数を実行するかをどのように決定しますかコールスタックの最初??一方が他方よりも優先されるでしょうか?
回答: 同時完了の場合、タイマー コールバックは I/O コールバックの前に実行されます。
これまで多くのことを学習してきましたが、下の図に示されている実行シーケンスを念頭に置いていただければ幸いです。これは、Node.js が舞台裏で何を行っているかを完全に示しているためです。非同期コードを実行する方法。
しかし、「このビジュアライゼーションを検証するコードはどこにあるの?」と疑問に思うかもしれません。イベント ループ内の各キューには実装上のニュアンスがあるため、それらについて 1 つずつ説明するほうがよいでしょう。この記事は、Node.js イベント ループに関するシリーズの最初の記事です。各キューの操作の詳細については、記事の最後にあるリンクを必ず確認してください。今は心に深い印象が残っている場合でも、特定のシナリオに到達すると、いくつかの罠に陥る可能性があります。 。
このビジュアル ガイドでは、JavaScript の非同期プログラミング、Node.js ランタイム、および非同期操作の処理を担当する libuv の基本について説明します。この知識があれば、Node.js の非同期の性質を利用するコードを作成するときに役立つ強力なイベント ループ モデルを構築できます。
ノード関連の知識の詳細については、nodejs チュートリアル を参照してください。
以上がNodeのイベントループについて話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。