Node8 における AsyncHooks 非同期ライフサイクル

Node8 における AsyncHooks 非同期ライフサイクル

Async Hooks は Node8 の新機能です。NodeJs の非同期リソースのライフサイクルを追跡するための API をいくつか提供します。これは NodeJs の組み込みモジュールであり、直接参照できます。

定数 async_hooks = require('async_hooks');

これはめったに使用されないモジュールですが、なぜ存在するのでしょうか?

ご存知のとおり、JavaScript は最初からシングルスレッド言語として設計されています。これは、JavaScript の元々の設計意図に関係しています。元々の JavaScript は、ネットワーク速度が低かった時代に、ユーザーがサーバーの応答を待つ時間コストを削減するために、ページ上のフォーム検証を実行するためにのみ使用されていました。 Web フロントエンド技術の発展に伴い、フロントエンド機能はますます強力になり、ますます重要になっていますが、シングルスレッドで解決できない問題はないようです。比較すると、マルチスレッドはより複雑であるように思われるため、現在でもシングルスレッドが使用されています。

JavaScript はシングルスレッドなので、日常の開発ではタイマーや現在標準化されている Ajax など、時間のかかるタスクが常に存在します。これらの問題を解決するために、JavaScript は BOM、DOM、ECMAScript に分かれています。BOM は、非同期タスクと呼ばれる時間のかかるタスクを解決するのに役立ちます。

ブラウザの BOM は非同期タスクの処理に役立つため、ほとんどのプログラマーは非同期タスクの使用方法以外についてほとんど何も知りません。たとえば、同時にキューにある非同期タスクの数はいくつでしょうか?非同期処理が混雑しているかどうかなどの関連情報を直接取得する方法はありません。多くの場合、基盤となるレイヤーでは関連情報に注意を払う必要はありません。ただし、場合によっては関連情報が必要な場合に備えて、NodeJS では async_hooks という実験的な API が提供されています。なぜ NodeJS か? タイマーや http などの非同期モジュールを開発者が制御できるのは Node だけだからです。ブラウザーが対応する API を提供しない限り、ブラウザーの BOM は開発者によって制御されません。

async_hooksルール

async_hooks は、各関数がコンテキスト (非同期スコープと呼ばれる) を提供することを規定しています。各非同期スコープには、現在の非同期スコープのロゴである asyncId があります。同じ非同期スコープ内の asyncId は同じである必要があります。

複数の非同期タスクが並行して実行されている場合、asyncId を使用すると、どの非同期タスクを監視するかを区別できます。

asyncId は、自己増加する非反復の正の整数です。プログラムの最初の asyncId は 1 である必要があります。

簡単に言うと、async スコープは中断できない同期タスクです。中断できない限り、コードがどれだけ長くても asyncId を共有します。ただし、途中でコールバックや await など中断できる場合は、新しい非同期コンテキストが作成され、新しい asyncId が作成されます。

各非同期スコープには、現在の関数がその非同期スコープによってトリガーされることを示す triggerAsyncId があります。

asyncId と triggerAsyncId を使用すると、非同期呼び出しの関係とリンク全体を簡単に追跡できます。

async_hooks.executionAsyncId() は asyncId を取得するために使用されます。グローバル asyncId が 1 であることがわかります。

async_hooks.triggerAsyncId() は triggerAsyncId を取得するために使用され、その現在の値は 0 です。

定数 async_hooks = require('async_hooks');
console.log('asyncId:', async_hooks.executionAsyncId()); // asyncId: 1
console.log('triggerAsyncId:', async_hooks.triggerAsyncId()); // トリガーAsyncId: 0

ここでは、 fs.open を使用してファイルを開きます。 fs.open の asyncId は 7 で、 fs.open の triggerAsyncId は 1 になることがわかります。これは、 fs.open がグローバル呼び出しによってトリガーされ、グローバル asyncId が 1 であるためです。

定数 async_hooks = require('async_hooks');
console.log('asyncId:', async_hooks.executionAsyncId()); // asyncId: 1
console.log('triggerAsyncId:', async_hooks.triggerAsyncId()); // トリガーAsyncId: 0
定数 fs = require('fs');
fs.open('./test.js', 'r', (err, fd) => {
    console.log('fs.open.asyncId:', async_hooks.executionAsyncId()); // 7
    console.log('fs.open.triggerAsyncId:', async_hooks.triggerAsyncId()); // 1
});

非同期関数のライフサイクル

もちろん、実際のアプリケーションでは async_hooks はこのようには使用されません。正しい使用方法は、すべての非同期タスクが作成、実行、破棄される前、後、および後にコールバックをトリガーし、すべてのコールバックが asyncId で渡されることです。

async_hooks.createHook を使用して、非同期リソース フックを作成できます。このフックは、オブジェクトをパラメーターとして受け取り、非同期リソースのライフ サイクルで発生する可能性のあるイベントのコールバック関数を登録します。これらのフック関数は、非同期リソースが作成/実行/破棄されるたびにトリガーされます。

定数 async_hooks = require('async_hooks');

定数asyncHook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) { },
  破棄(非同期ID) { }
})

現在、createHook 関数は次の 5 種類のフック コールバックを受け入れることができます。

1. init(asyncId、type、triggerAsyncId、resource)

  • init コールバック関数は通常、非同期リソースが初期化されるときにトリガーされます。
  • asyncId: 各非同期リソースは一意の識別子を生成します
  • type: 非同期リソースのタイプ。通常はリソースのコンストラクターの名前です。

FSEVENTWRAP、FSREQCALLBACK、GETADDRINFOREQWRAP、GETNAMEINFOREQWRAP、HTTPINCOMINGMESSAGE、
HTTPCLIENTREQUEST、JSSTREAM、PIPECONNECTWRAP、PIPEWRAP、PROCESSWRAP、QUERYWRAP、
SHUTDOWNWRAP、SIGNALWRAP、STATWATCHER、TCPCONNECTWRAP、TCPSERVERWRAP、TCPWRAP、
TTYWRAP、UDPSENDWRAP、UDPWRAP、WRITEWRAP、ZLIB、SSLCONNECTION、PBKDF2REQUEST、
RANDOMBYTESREQUEST、TLSWRAP、マイクロタスク、タイムアウト、即時、TickObject

  • triggerAsyncId: 現在の非同期リソースの作成をトリガーする対応する非同期スコープの asyncId を示します。
  • リソース: 初期化される非同期リソースオブジェクトを表します

async_hooks.createHook 関数を使用して、各非同期リソースのライフサイクルで発生する init/before/after/destory/promiseResolve やその他の関連イベントのリスナー関数を登録できます。
同じ非同期スコープが複数回呼び出されて実行されることがあります。実行回数に関係なく、asyncId は同じである必要があります。監視機能を使用すると、実行回数と実行時間、オンライン コンテキストとの関係を簡単に追跡できます。

2. 前(非同期ID)

before 関数は、通常、asyncId に対応する非同期リソース操作が完了した後、コールバックが実行される前に呼び出されます。before コールバック関数は、コールバックされる回数によって複数回実行される場合があります。使用時にはこの点に注意してください。

3.after(非同期ID)

after コールバック関数は通常、非同期リソースがコールバック関数を実行した直後に呼び出されます。コールバック関数の実行中にキャッチされない例外が発生した場合は、「uncaughtException」イベントがトリガーされた後に after イベントが呼び出されます。

4.破棄(非同期ID)

asyncId に対応する非同期リソースが破棄されたときに呼び出されます。一部の非同期リソースの破棄はガベージ コレクション メカニズムに依存するため、メモリ リークが原因で、破棄イベントがトリガーされない場合があります。

5. promiseResolve(非同期ID)

Promise コンストラクターの resolve 関数が実行されると、promiseResolve イベントがトリガーされます。場合によっては、一部の解決関数が暗黙的に実行されます。たとえば、.then 関数は新しい Promise を返し、この時点でこれも呼び出されます。

定数 async_hooks = require('async_hooks');

// 現在の実行コンテキストの asyncId を取得します
eid は async_hooks.executionAsyncId() です。

// 現在の関数をトリガーする asyncId を取得します
定数 tid = async_hooks.triggerAsyncId();

// 新しい AsyncHook インスタンスを作成します。これらのコールバックはすべてオプションです。const asyncHook =
    async_hooks.createHook({ init、before、after、destroy、promiseResolve });

//AsyncHook.enable() を実行するには宣言する必要があります。

// 新しい非同期イベントのリッスンを無効にします。
asyncHook を無効にします。

関数 init(asyncId, type, triggerAsyncId, resource) { }

関数before(asyncId) { }

関数 after(asyncId) { }

関数destroy(asyncId) { }

関数promiseResolve(asyncId) { }

約束

Promise は特別なケースです。十分に注意すれば、init メソッドの型には PROMISE がないことがわかります。 Promise の asyncId を取得するために ah.executionAsyncId() のみを使用する場合、正しい ID を取得できません。実際のフックを追加した後にのみ、async_hooks は Promise コールバックの asyncId を作成します。

つまり、V8 では asyncId を取得するための実行コストが高いため、デフォルトでは Promise に新しい asyncId を割り当てません。
つまり、デフォルトでは、promise または async/await を使用すると、現在のコンテキストの正しい asyncId と triggerId を取得できません。しかし、それは問題ではありません。async_hooks.createHook(callbacks).enable() 関数を実行することで、asyncId を Promise に強制的に割り当てることができます。

定数 async_hooks = require('async_hooks');

定数asyncHook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) { },
  破棄(非同期ID) { }
})
asyncHook を有効にします。

Promise.resolve(123).then(() => {
  console.log(`asyncId ${async_hooks.executionAsyncId()} トリガーId ${async_hooks.triggerAsyncId()}`);
});

また、Promise は init および promiseResolve フックイベント関数のみをトリガーし、before イベントと after イベントのフック関数は Promise がチェーンされたときのみ、つまり .then/.catch 関数内で Promise が生成された場合にのみトリガーされます。

新しいPromise(resolve => {
    解決する(123);
})。次に、データ => {
    コンソールにログ出力します。
})

上記には 2 つの Promise があり、最初の Promise は新しいインスタンス化によって作成され、2 番目の Promise はその後に作成されることがわかります (わからない場合は、以前の Promise ソース コードの記事を参照してください)。

ここでの順序は、新しい Promise を実行するときに、独自の init 関数が呼び出され、解決時に promiseResolve 関数が呼び出されます。次に、then メソッドで 2 番目の Promise の init 関数を実行し、次に 2 番目の Promise の before、promiseResovle、および after 関数を実行します。

例外処理

登録された async-hook コールバック関数で例外が発生した場合、サービスはエラー ログを出力し、直ちに終了します。同時に、すべてのリスナーが削除され、プログラムを終了させるための 'exit' イベントがトリガーされます。

プロセスがすぐに終了する理由は、これらの async-hook 関数が不安定に実行されると、次に同じイベントがトリガーされたときに例外がスローされる可能性が高いためです。これらの関数は主に非同期イベントを監視するために使用されます。不安定な場合は、適時に検出して修正する必要があります。

非同期フックコールバックでログを出力する

console.log 関数も非同期呼び出しであるため、async-hook 関数で console.log を再度呼び出すと、対応するフック イベントが再度トリガーされ、無限ループ呼び出しが発生します。したがって、async-hook 関数で追跡するには、同期ログを使用する必要があります。fs.writeSync 関数を使用できます。

定数 fs = require('fs');
'util' が必要です。

関数debug(...args) {
  fs.writeFileSync('log.out', `${util.format(...args)}\n`, { フラグ: 'a' });
}

[参考資料 - AsyncHooks] (https://nodejs.org/dist/latest-v15.x/docs/api/async_hooks.html)

Node8 の AsyncHooks の非同期ライフサイクルに関するこの記事はこれで終わりです。Node AsyncHooks の非同期ライフサイクルに関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Node.js http モジュールの使用
  • Nodejs 探索: シングルスレッドの高並行性の原理を深く理解する
  • Node.jsを理解するのはとても簡単です
  • node.js グローバル変数の具体的な使用法
  • Nodejs エラー処理プロセス記録
  • Expressを使用してプロジェクトを自動的にビルドするNode.jsのプロセス全体
  • ノードでシェルスクリプトを使用する方法
  • Node.js の TCP 接続処理のコア プロセス
  • Nodejs 配列キューと forEach アプリケーションの詳細な説明
  • Node.jsとDenoの比較

<<:  Alibaba Cloud Centos7のインストールとSVNの設定

>>:  Centos7.3 での mysql5.7 のインストールと設定のチュートリアル

推薦する

HTML でフォントの色を設定する方法と、PS を使用して HTML で正確なフォントの色を取得する方法

1. HTMLフォントカラー設定HTML では、フォント タグを使用してフォント コンテンツの色を設...

Linux の MySQL 設定の変更が有効にならない問題の解決方法

背景AWS EC2 を使用するプロジェクトサービスがあります。セキュリティとパフォーマンスを考慮して...

CSSスクロールバーのスタイルをカスタマイズする方法の詳細な説明

この記事では、CSS スクロールバー セレクターを紹介し、Webkit ブラウザーと IE ブラウザ...

Vue3とVue2の利点のまとめ

目次1. なぜ vue3 が必要なのでしょうか? 2. vue3の利点3. 応答原則の違い4. ライ...

Linux での MySQL 8.0.25 のインストールと設定のチュートリアル

LinuxにMySQL 8.0.25をインストールするための最新のチュートリアルを参考にしてください...

MySQL 5.7 スレーブノードからマルチスレッド マスター スレーブ レプリケーションを構成する方法の詳細な説明

序文MySQL は MySQL 5.6 からマルチスレッド レプリケーションをサポートしていますが、...

nginx アンチホットリンクおよびアンチクローラー設定の詳細な説明

新しい設定ファイルを作成します (たとえば、nginx インストール ディレクトリの下の conf ...

最新の MySQL 5.7.23 のインストールと設定のグラフィックチュートリアル

2018 年の最新 MySQL 5.7 の詳細なインストールと設定は 4 つのステップに分かれており...

Tik Tok サブスクリプション ボタンのアニメーション効果を実現する CSS

少し前にTik Tokを見ていて、フォローするときのボタンアニメーションがとても美しいと思ったのと、...

カルーセル例の JavaScript 実装

この記事では、カルーセルの効果を実現するためのJavaScriptの具体的なコードを参考までに共有し...

Mac OS 10.11 での MySQL 5.7.12 のインストールと設定のチュートリアル

Mac OS 10.11 に MySQL をインストールして設定する方法を、主に写真を使って手順を簡...

インデックススキャンを使用したMySQLソート

目次sakilaをインストールするインデックススキャンソートテーブル構造インデックススキャンをソート...

Dockerを使用してGitlabを素早くデプロイする方法

1. GitLabイメージをダウンロードする docker pull gitlab/gitlab-c...

nginxとIISで使用できるSSL証明書を作成する

目次SSL証明書の作成1. 秘密鍵を生成する2. 証明書要求ファイルを生成する3. CRT証明書ファ...