Node.js のタイマーについての簡単な説明

高洛峰
リリース: 2017-02-08 16:18:26
オリジナル
1055 人が閲覧しました

この記事では、Node.js のタイマーに関する関連情報を共有します。必要な方は参照してください。

Node.jsでのタイマーの実装

前回のブログ投稿では、Nodeのタイマーは新しいスレッドを開くことで実装されるのではなく、イベントループ内で直接完了すると述べました。以下では、いくつかの JavaScript タイマーの例と Node 関連のソース コードを使用して、タイマー関数が Node でどのように実装されるかを分析します。

JavaScriptのタイマー関数の特徴

Nodeでもブラウザでも、setTimeoutとsetIntervalという2つのタイマー関数があり、その動作特性は基本的に同じなので、以下ではNodeを例として説明します。分析。

JavaScript のタイマーは、コンピューターの基本的なスケジュールされた割り込みと変わらないことがわかっています。割り込みが到着すると、現在実行中のコードが中断され、スケジュールされた割り込み処理関数に転送されます。 JavaScript タイマーが期限切れになると、現在の実行スレッドに実行中のコードがない場合、対応するコールバック関数が実行されます。現在実行中のコードがある場合、JavaScript エンジンはコールバックを実行するために現在のコードを中断しません。 start 新しいスレッドはコールバックを実行しますが、現在のコードが実行された後に処理されます。

console.time('A')
setTimeout(function () {
  console.timeEnd('A');
}, 100);
var i = 0;
for (; i < 100000; i++) { }
ログイン後にコピー

上記のコードを実行すると、最終的な出力時間が約100ミリ秒ではなく、数秒であることがわかります。これは、スケジュールされたコールバック関数が実際にはループが完了する前に実行されず、ループの終わりまで延期されることを示しています。実際、JavaScript コードの実行中は、すべてのイベントを処理することはできず、現在のコードが完了するまで新しいイベントを処理することはできません。これが、時間のかかる JavaScript コードをブラウザで実行するとブラウザが応答しなくなる理由です。この状況に対処するには、Yielding Processes テクニックを使用して、時間のかかるコードを小さなチャンク (チャンク) に分割し、各チャンクが処理された後に setTimeout を 1 回実行し、短期間の後に次のチャンクを処理することに同意します。この期間中、アイドル時間中、ブラウザ/ノードはキューに入れられたイベントを処理できます。

補足情報

高度なタイマーと生成プロセスについては、第 22 章 JavaScript 高度なプログラミングの高度なテクニック、第 3 版で詳しく説明されています。

Nodeでのタイマー実装

libuvのuv_loop_t型の初期化

前回のブログ投稿では、Nodeがlibuvのuv_run関数を呼び出してイベントスケジューリングのためにdefault_loop_ptrを開始し、default_loop_ptrはuv_loop_t型の変数default_loop_structを指すと述べました。ノードが起動すると、uv_loop_init(&default_loop_struct) を呼び出して初期化します。 uv_loop_init 関数の抜粋は次のとおりです。

int uv_loop_init(uv_loop_t* loop) {
 ...
 loop->time = 0;
 uv_update_time(loop);
 ...
}
ログイン後にコピー

ループの time フィールドが最初に 0 に割り当てられ、次に uv_update_time が割り当てられることがわかります。関数が呼び出され、最新のカウントが更新されます。 time は、loop.time に割り当てられます。

初期化が完了すると、default_loop_struct.time には初期値が設定され、時間関連の操作はこの値と比較され、対応するコールバック関数を呼び出すかどうかが決定されます。

libuv のイベント スケジューリング コア

前述したように、uv_run 関数は libuv ライブラリのイベント ループ実装のコア部分です。以下はそのフローチャートです:

Node.js のタイマーについての簡単な説明

上記のロジックの簡単な説明は次のとおりです。タイマーに関連するもの:

現在のループの概念で「現在」をマークする現在のループの時間フィールドを更新します。

ループが生きているかどうかを確認します。つまり、タスク (ハンドラー/) があるかどうかを確認します。ループ内で処理する必要があるリクエスト)、そうでない場合はループする必要はありません。
タイマーに指定された時間が現在よりも遅れている場合は、タイマーが期限切れになったことを意味します。コールバック関数が実行されます。
I/O ポーリングを実行します (つまり、スレッドをブロックし、I/O イベントが発生するのを待ちます)。次のタイマーが期限切れになったときに I/O が完了していない場合は、待機を停止して次のタイマー コールバックを実行します。 。

I/O イベントが発生すると、対応するコールバックが実行されます。コールバックの実行中に別のタイマーが期限切れになる可能性があるため、タイマーを再度チェックしてコールバックを実行する必要があります。
(実際には、ここでの (4.) は、単なるワンステップ操作ではなく、より複雑です。この説明は、他の詳細を含まず、タイマーの実装に焦点を当てているだけです。)
ノードは、ループが終了するまで uv_run を呼び出し続けます。生きている。

Nodeのtimer_wrapとタイマー

NodeにはTimerWrapクラスがあり、Node内でtimer_wrapモジュールとして登録されています。

NODE_MODULE_CONTEXT_AWARE_BUILTIN(timer_wrap、node::TimerWrap::Initialize)
TimerWrap クラスは基本的に uv_timer_t を直接カプセル化したもので、NODE_MODULE_CONTEXT_AWARE_BUILTIN は組み込みモジュールを登録するために Node によって使用されるマクロです。

このステップの後、JavaScript はこのモジュールを動作させることができます。 src/lib/timers.js ファイルは、JavaScript を使用して timer_wrap 関数をカプセル化し、exports.setTimeout、exports.setInterval、exports.setImmediate およびその他の関数をエクスポートします。

ノードの起動とグローバル初期化

前の記事では、Node が開始時に実行環境 LoadEnvironment(env) をロードすると述べました。この関数の非常に重要なステップは、src/node.js をロードして実行することです。src/node.js は指定されたモジュールをロードし、グローバルを初期化して処理します。もちろん、setTimeout などの関数も src/node.js によってグローバル オブジェクトにバインドされます。

以上がこの記事の全内容です。皆さんに気に入っていただければ幸いです。

Node.js のタイマーに関連するその他の記事については、PHP 中国語 Web サイトに注目してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート