Node.js v21.7.2 ドキュメント
- Node.js v21.7.2
- ► 目次
-
► インデックス
- アサーションテスト
- 非同期コンテキスト追跡
- 非同期フック
- バッファ
- C++ アドオン
- Node-API を使用した C/C++ アドオン
- C++ 埋め込み API
- 子プロセス
- クラスタ
- コマンドラインオプション
- コンソール
- Corepack
- 暗号
- デバッガ
- 非推奨の API
- 診断チャネル
- DNS
- ドメイン
- エラー
- イベント
- ファイルシステム
- グローバル
- HTTP
- HTTP/2
- HTTPS
- インスペクタ
- 国際化
- モジュール: CommonJS モジュール
- モジュール: ECMAScript モジュール
- モジュール:
node:module
API - モジュール: パッケージ
- Net
- OS
- パス
- パフォーマンスフック
- パーミッション
- プロセス
- Punycode
- クエリ文字列
- Readline
- REPL
- レポート
- 単一実行可能ファイルアプリケーション
- ストリーム
- 文字列デコーダ
- テストランナー
- タイマー
- TLS/SSL
- トレースイベント
- TTY
- UDP/データグラム
- URL
- ユーティリティ
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- ワーカースレッド
- Zlib
- ► 他のバージョン
- ► オプション
非同期フック#
createHook
、AsyncHook
、executionAsyncResource
API は、使いやすさの問題、安全上のリスク、パフォーマンスへの影響があるため、使用しないことをお勧めします。 非同期コンテキスト追跡のユースケースは、安定した AsyncLocalStorage
API によってより適切に処理されます。 AsyncLocalStorage
で解決されるコンテキスト追跡のニーズ、または 診断チャネル によって現在提供されている診断データを超える createHook
、AsyncHook
、または executionAsyncResource
のユースケースがある場合は、https://github.com/nodejs/node/issues にイシューを開き、ユースケースを説明して、より目的に特化した API を作成できるようにしてください。ソースコード: lib/async_hooks.js
async_hooks
API の使用は強くお勧めしません。そのユースケースのほとんどをカバーできる他の API には、次のものがあります。
AsyncLocalStorage
は非同期コンテキストを追跡しますprocess.getActiveResourcesInfo()
はアクティブなリソースを追跡します
node:async_hooks
モジュールは、非同期リソースを追跡するための API を提供します。 これは、以下を使用してアクセスできます。
import async_hooks from 'node:async_hooks';
const async_hooks = require('node:async_hooks');
用語#
非同期リソースは、関連付けられたコールバックを持つオブジェクトを表します。 このコールバックは、net.createServer()
の 'connection'
イベントのように複数回呼び出される場合もあれば、fs.open()
のように 1 回だけ呼び出される場合もあります。 また、コールバックが呼び出される前にリソースが閉じられることもあります。 AsyncHook
は、これらの異なるケースを明示的に区別するのではなく、リソースという抽象的な概念として表現します。
Worker
が使用されている場合、各スレッドは独立した async_hooks
インターフェースを持ち、各スレッドは新しい非同期 ID のセットを使用します。
概要#
以下は、パブリック API の簡単な概要です。
import async_hooks from 'node:async_hooks';
// Return the ID of the current execution context.
const eid = async_hooks.executionAsyncId();
// Return the ID of the handle responsible for triggering the callback of the
// current execution scope to call.
const tid = async_hooks.triggerAsyncId();
// Create a new AsyncHook instance. All of these callbacks are optional.
const asyncHook =
async_hooks.createHook({ init, before, after, destroy, promiseResolve });
// Allow callbacks of this AsyncHook instance to call. This is not an implicit
// action after running the constructor, and must be explicitly run to begin
// executing callbacks.
asyncHook.enable();
// Disable listening for new asynchronous events.
asyncHook.disable();
//
// The following are the callbacks that can be passed to createHook().
//
// init() is called during object construction. The resource may not have
// completed construction when this callback runs. Therefore, all fields of the
// resource referenced by "asyncId" may not have been populated.
function init(asyncId, type, triggerAsyncId, resource) { }
// before() is called just before the resource's callback is called. It can be
// called 0-N times for handles (such as TCPWrap), and will be called exactly 1
// time for requests (such as FSReqCallback).
function before(asyncId) { }
// after() is called just after the resource's callback has finished.
function after(asyncId) { }
// destroy() is called when the resource is destroyed.
function destroy(asyncId) { }
// promiseResolve() is called only for promise resources, when the
// resolve() function passed to the Promise constructor is invoked
// (either directly or through other means of resolving a promise).
function promiseResolve(asyncId) { }
const async_hooks = require('node:async_hooks');
// Return the ID of the current execution context.
const eid = async_hooks.executionAsyncId();
// Return the ID of the handle responsible for triggering the callback of the
// current execution scope to call.
const tid = async_hooks.triggerAsyncId();
// Create a new AsyncHook instance. All of these callbacks are optional.
const asyncHook =
async_hooks.createHook({ init, before, after, destroy, promiseResolve });
// Allow callbacks of this AsyncHook instance to call. This is not an implicit
// action after running the constructor, and must be explicitly run to begin
// executing callbacks.
asyncHook.enable();
// Disable listening for new asynchronous events.
asyncHook.disable();
//
// The following are the callbacks that can be passed to createHook().
//
// init() is called during object construction. The resource may not have
// completed construction when this callback runs. Therefore, all fields of the
// resource referenced by "asyncId" may not have been populated.
function init(asyncId, type, triggerAsyncId, resource) { }
// before() is called just before the resource's callback is called. It can be
// called 0-N times for handles (such as TCPWrap), and will be called exactly 1
// time for requests (such as FSReqCallback).
function before(asyncId) { }
// after() is called just after the resource's callback has finished.
function after(asyncId) { }
// destroy() is called when the resource is destroyed.
function destroy(asyncId) { }
// promiseResolve() is called only for promise resources, when the
// resolve() function passed to the Promise constructor is invoked
// (either directly or through other means of resolving a promise).
function promiseResolve(asyncId) { }
async_hooks.createHook(callbacks)
#
callbacks
<Object> 登録する フックコールバックinit
<Function>init
コールバック。before
<Function>before
コールバック。after
<Function>after
コールバック。destroy
<Function>destroy
コールバック。promiseResolve
<Function>promiseResolve
コールバック。
- 戻り値: <AsyncHook> フックの無効化と有効化に使用されるインスタンス
各非同期操作のさまざまなライフタイムイベントに対して呼び出される関を登録します。
コールバック init()
/before()
/after()
/destroy()
は、リソースのライフタイム中に、それぞれの非同期イベントに対して呼び出されます。
すべてのコールバックはオプションです。 たとえば、リソースのクリーンアップのみを追跡する必要がある場合は、destroy
コールバックのみを渡す必要があります。 callbacks
に渡すことができるすべての関数の詳細は、フックコールバック セクションにあります。
import { createHook } from 'node:async_hooks';
const asyncHook = createHook({
init(asyncId, type, triggerAsyncId, resource) { },
destroy(asyncId) { },
});
const async_hooks = require('node:async_hooks');
const asyncHook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) { },
destroy(asyncId) { },
});
コールバックは、プロトタイプチェーンを介して継承されます
class MyAsyncCallbacks {
init(asyncId, type, triggerAsyncId, resource) { }
destroy(asyncId) {}
}
class MyAddedCallbacks extends MyAsyncCallbacks {
before(asyncId) { }
after(asyncId) { }
}
const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
Promise は、ライフサイクルが非同期フックメカニズムによって追跡される非同期リソースであるため、init()
、before()
、after()
、destroy()
コールバックは、Promise を返す非同期関数であってはなりません。
エラー処理#
AsyncHook
コールバックがスローされた場合、アプリケーションはスタックトレースを出力して終了します。 終了パスはキャッチされていない例外のパスに従いますが、すべての 'uncaughtException'
リスナーが削除されるため、プロセスは強制終了されます。 アプリケーションが --abort-on-uncaught-exception
で実行されている場合を除き、'exit'
コールバックは引き続き呼び出されます。 この場合、スタックトレースが出力され、アプリケーションはコアファイルを will 残して終了します。
このエラー処理動作の理由は、これらのコールバックがオブジェクトのライフタイムにおいて、たとえばクラスの構築と破棄中に、潜在的に不安定なポイントで実行されているためです。 このため、将来意図しない中止を防ぐために、プロセスを迅速に停止する必要があると判断されます。 例外が意図しない副作用なしに通常の制御フローに従うことができることを確認するための包括的な分析が実行された場合、これは将来変更される可能性があります。
AsyncHook
コールバックでの出力#
コンソールへの出力は非同期操作であるため、console.log()
は AsyncHook
コールバックの呼び出しを引き起こします。 AsyncHook
コールバック関数内で console.log()
または同様の非同期操作を使用すると、無限再帰が発生します。 デバッグ時にこれを簡単に解決するには、fs.writeFileSync(file, msg, flag)
などの同期ログ操作を使用します。 これはファイルに出力され、同期であるため、AsyncHook
を再帰的に呼び出しません。
import { writeFileSync } from 'node:fs';
import { format } from 'node:util';
function debug(...args) {
// Use a function like this one when debugging inside an AsyncHook callback
writeFileSync('log.out', `${format(...args)}\n`, { flag: 'a' });
}
const fs = require('node:fs');
const util = require('node:util');
function debug(...args) {
// Use a function like this one when debugging inside an AsyncHook callback
fs.writeFileSync('log.out', `${util.format(...args)}\n`, { flag: 'a' });
}
ロギングに非同期操作が必要な場合は、AsyncHook
自体によって提供される情報を使用して、非同期操作の原因を追跡できます。 ロギング自体が AsyncHook
コールバックの呼び出しを引き起こした場合、ロギングはスキップする必要があります。 これにより、そうでなければ無限再帰が中断されます。
クラス: AsyncHook
#
クラス AsyncHook
は、非同期操作のライフタイムイベントを追跡するためのインターフェースを公開します。
asyncHook.enable()
#
- 戻り値: <AsyncHook>
asyncHook
への参照。
指定された AsyncHook
インスタンスのコールバックを有効にします。 コールバックが提供されない場合、有効化は操作なしです。
AsyncHook
インスタンスはデフォルトで無効になっています。 作成直後に AsyncHook
インスタンスを有効にする必要がある場合は、次のパターンを使用できます。
import { createHook } from 'node:async_hooks';
const hook = createHook(callbacks).enable();
const async_hooks = require('node:async_hooks');
const hook = async_hooks.createHook(callbacks).enable();
asyncHook.disable()
#
- 戻り値: <AsyncHook>
asyncHook
への参照。
実行される AsyncHook
コールバックのグローバルプールから、指定された AsyncHook
インスタンスのコールバックを無効にします。 フックが無効化されると、有効になるまで再び呼び出されません。
API の整合性のために、disable()
も AsyncHook
インスタンスを返します。
フックコールバック#
非同期イベントのライフタイムにおける重要なイベントは、インスタンス化、コールバックが呼び出される前/後、インスタンスが破棄されるときの 4 つの領域に分類されています。
init(asyncId, type, triggerAsyncId, resource)
#
asyncId
<number> 非同期リソースの一意の ID。type
<string> 非同期リソースのタイプ。triggerAsyncId
<number> この非同期リソースが作成された実行コンテキスト内での、非同期リソースの一意のID。resource
<Object> 非同期操作を表すリソースへの参照。destroy 中に解放する必要があります。
非同期イベントを発行する可能性のあるクラスが構築されたときに呼び出されます。これは、インスタンスが destroy
が呼び出される前に before
/after
を呼び出す必要があることを意味するのではなく、その可能性があることを意味します。
この動作は、リソースを開いてから、リソースを使用できるようになる前に閉じることで観察できます。次のスニペットはこれを示しています。
import { createServer } from 'node:net';
createServer().listen(function() { this.close(); });
// OR
clearTimeout(setTimeout(() => {}, 10));
require('node:net').createServer().listen(function() { this.close(); });
// OR
clearTimeout(setTimeout(() => {}, 10));
新しいリソースごとに、現在のNode.jsインスタンスのスコープ内で一意のIDが割り当てられます。
type
#
type
は、init
の呼び出しの原因となったリソースのタイプを識別する文字列です。一般的に、リソースのコンストラクターの名前に対応します。
Node.js自体によって作成されたリソースの type
は、Node.jsのリリースごとに変更される可能性があります。有効な値には、TLSWRAP
、TCPWRAP
、TCPSERVERWRAP
、GETADDRINFOREQWRAP
、FSREQCALLBACK
、Microtask
、Timeout
などがあります。使用しているNode.jsバージョンのソースコードを調べて、完全なリストを取得してください。
さらに、AsyncResource
のユーザーは、Node.js自体とは独立して非同期リソースを作成します。
PROMISE
リソースタイプもあります。これは、Promise
インスタンスとそれらによってスケジュールされた非同期作業を追跡するために使用されます。
ユーザーは、パブリックエンベダーAPIを使用するときに独自の type
を定義できます。
型名の衝突が発生する可能性があります。エンベダーは、フックをリッスンする際の衝突を防ぐために、npmパッケージ名などの固有のプレフィックスを使用することをお勧めします。
triggerAsyncId
#
triggerAsyncId
は、新しいリソースの初期化と init
の呼び出しを発生させた(または「トリガーした」)リソースの asyncId
です。これは、リソースがいつ作成されたかのみを示す async_hooks.executionAsyncId()
とは異なり、triggerAsyncId
はリソースがなぜ作成されたかを示します。
以下は、triggerAsyncId
の簡単なデモです。
import { createHook, executionAsyncId } from 'node:async_hooks';
import { stdout } from 'node:process';
import net from 'node:net';
import fs from 'node:fs';
createHook({
init(asyncId, type, triggerAsyncId) {
const eid = executionAsyncId();
fs.writeSync(
stdout.fd,
`${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`);
},
}).enable();
net.createServer((conn) => {}).listen(8080);
const { createHook, executionAsyncId } = require('node:async_hooks');
const { stdout } = require('node:process');
const net = require('node:net');
const fs = require('node:fs');
createHook({
init(asyncId, type, triggerAsyncId) {
const eid = executionAsyncId();
fs.writeSync(
stdout.fd,
`${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`);
},
}).enable();
net.createServer((conn) => {}).listen(8080);
nc localhost 8080
でサーバーにアクセスしたときの出力
TCPSERVERWRAP(5): trigger: 1 execution: 1
TCPWRAP(7): trigger: 5 execution: 0
TCPSERVERWRAP
は、接続を受信するサーバーです。
TCPWRAP
は、クライアントからの新しい接続です。新しい接続が確立されると、TCPWrap
インスタンスがすぐに構築されます。これは、JavaScriptスタックの外部で発生します。(executionAsyncId()
が 0
であるということは、C++ から実行されており、その上に JavaScript スタックがないことを意味します。)その情報だけでは、リソースをそれらが作成された原因と関連付けることは不可能であるため、triggerAsyncId
には、新しいリソースの存在にどのリソースが関与しているかを伝播させるタスクが与えられます。
resource
#
resource
は、初期化された実際の非同期リソースを表すオブジェクトです。オブジェクトにアクセスするためのAPIは、リソースの作成者によって指定される場合があります。Node.js自体によって作成されたリソースは内部用であり、いつでも変更される可能性があります。したがって、これらのためのAPIは指定されていません。
パフォーマンス上の理由から、resourceオブジェクトが再利用される場合があり、`WeakMap` のキーとして使用したり、プロパティを追加したりすることは安全ではありません。
非同期コンテキストの例#
コンテキスト追跡のユースケースは、安定版API AsyncLocalStorage
でカバーされています。この例は非同期フックの動作を示しているだけですが、AsyncLocalStorage
はこのユースケースにより適しています。
以下は、before
と after
の呼び出しの間の init
の呼び出しに関する追加情報、具体的には listen()
へのコールバックがどのようになるかを示す例です。呼び出しコンテキストが見やすくなるように、出力のフォーマットが少し詳しくなっています。
import async_hooks from 'node:async_hooks';
import fs from 'node:fs';
import net from 'node:net';
import { stdout } from 'node:process';
const { fd } = stdout;
let indent = 0;
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
const eid = async_hooks.executionAsyncId();
const indentStr = ' '.repeat(indent);
fs.writeSync(
fd,
`${indentStr}${type}(${asyncId}):` +
` trigger: ${triggerAsyncId} execution: ${eid}\n`);
},
before(asyncId) {
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}before: ${asyncId}\n`);
indent += 2;
},
after(asyncId) {
indent -= 2;
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}after: ${asyncId}\n`);
},
destroy(asyncId) {
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}destroy: ${asyncId}\n`);
},
}).enable();
net.createServer(() => {}).listen(8080, () => {
// Let's wait 10ms before logging the server started.
setTimeout(() => {
console.log('>>>', async_hooks.executionAsyncId());
}, 10);
});
const async_hooks = require('node:async_hooks');
const fs = require('node:fs');
const net = require('node:net');
const { fd } = process.stdout;
let indent = 0;
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
const eid = async_hooks.executionAsyncId();
const indentStr = ' '.repeat(indent);
fs.writeSync(
fd,
`${indentStr}${type}(${asyncId}):` +
` trigger: ${triggerAsyncId} execution: ${eid}\n`);
},
before(asyncId) {
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}before: ${asyncId}\n`);
indent += 2;
},
after(asyncId) {
indent -= 2;
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}after: ${asyncId}\n`);
},
destroy(asyncId) {
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}destroy: ${asyncId}\n`);
},
}).enable();
net.createServer(() => {}).listen(8080, () => {
// Let's wait 10ms before logging the server started.
setTimeout(() => {
console.log('>>>', async_hooks.executionAsyncId());
}, 10);
});
サーバーを起動しただけの出力
TCPSERVERWRAP(5): trigger: 1 execution: 1
TickObject(6): trigger: 5 execution: 1
before: 6
Timeout(7): trigger: 6 execution: 6
after: 6
destroy: 6
before: 7
>>> 7
TickObject(8): trigger: 7 execution: 7
after: 7
before: 8
after: 8
例に示すように、executionAsyncId()
と execution
はそれぞれ、現在の`実行コンテキストの値を指定します。これは、`before` と `after` の呼び出しによって区切られます。
リソース割り当てのグラフ化に execution
のみを使用すると、次のようになります。
root(1)
^
|
TickObject(6)
^
|
Timeout(7)
TCPSERVERWRAP
は、console.log()
が呼び出された理由であったにもかかわらず、このグラフの一部ではありません。これは、ホスト名なしでポートにバインドすることは*同期*操作ですが、完全に非同期の API を維持するために、ユーザーのコールバックは `process.nextTick()` に配置されるためです。そのため、`TickObject` が出力に存在し、`.listen()` コールバックの「親」となっています。
グラフはリソースが*いつ*作成されたかのみを示し、*なぜ*かは示しません。そのため、*なぜ*を追跡するには `triggerAsyncId` を使用します。これは、次のグラフで表すことができます。
bootstrap(1)
|
˅
TCPSERVERWRAP(5)
|
˅
TickObject(6)
|
˅
Timeout(7)
before(asyncId)
#
asyncId
<number>
非同期操作が開始されたとき(TCPサーバーが新しい接続を受信するなど)または完了したとき(ディスクにデータを書き込むなど)、ユーザーに通知するためにコールバックが呼び出されます。before
コールバックは、上記のコールバックが実行される直前に呼び出されます。asyncId
は、コールバックを実行しようとしているリソースに割り当てられた一意の識別子です。
before
コールバックは 0 から N 回呼び出されます。非同期操作がキャンセルされた場合、またはたとえば TCP サーバーが接続を受信しない場合、before
コールバックは通常 0 回呼び出されます。TCP サーバーなどの永続的な非同期リソースは通常、before
コールバックを複数回呼び出しますが、`fs.open()` などの他の操作は 1 回だけ呼び出します。
after(asyncId)
#
asyncId
<number>
before
で指定されたコールバックが完了した直後に呼び出されます。
コールバックの実行中にキャッチされない例外が発生した場合、after
は 'uncaughtException'
イベントが発行された後、または domain
のハンドラーが実行された後に実行されます。
destroy(asyncId)
#
asyncId
<number>
asyncId
に対応するリソースが破棄された後に呼び出されます。また、エンベダー API `emitDestroy()` から非同期的に呼び出されます。
一部のリソースはクリーンアップにガベージコレクションに依存しているため、`init` に渡された `resource` オブジェクトへの参照が行われた場合、`destroy` が呼び出されない可能性があり、アプリケーションでメモリリークが発生する可能性があります。リソースがガベージコレクションに依存していない場合、これは問題になりません。
destroy フックを使用すると、ガベージコレクターを介して `Promise` インスタンスの追跡が有効になるため、オーバーヘッドが増加します。
promiseResolve(asyncId)
#
asyncId
<number>
Promise
コンストラクターに渡された `resolve` 関数が呼び出されたとき(直接または promise を解決する他の手段を介して)に呼び出されます。
resolve()
は、目に見える同期作業を行いません。
別の `Promise` の状態を想定することで `Promise` が解決された場合、この時点で `Promise` が必ずしも履行または拒否されるわけではありません。
new Promise((resolve) => resolve(true)).then((a) => {});
以下のコールバックを呼び出します
init for PROMISE with id 5, trigger id: 1
promise resolve 5 # corresponds to resolve(true)
init for PROMISE with id 6, trigger id: 5 # the Promise returned by then()
before 6 # the then() callback is entered
promise resolve 6 # the then() callback resolves the promise by returning
after 6
async_hooks.executionAsyncResource()
#
- 戻り値: <Object> 現在の`実行を表すリソース。リソース内にデータを格納するのに役立ちます。
`executionAsyncResource()` によって返されるリソースオブジェクトは、ほとんどの場合、ドキュメント化されていない API を持つ内部 Node.js ハンドルオブジェクトです。オブジェクトの関数やプロパティを使用すると、アプリケーションがクラッシュする可能性が高いため、避けてください。
トップレベルの実行コンテキストで `executionAsyncResource()` を使用すると、使用できるハンドルまたはリクエストオブジェクトがないため、空のオブジェクトが返されますが、トップレベルを表すオブジェクトがあると役立つ場合があります。
import { open } from 'node:fs';
import { executionAsyncId, executionAsyncResource } from 'node:async_hooks';
console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
open(new URL(import.meta.url), 'r', (err, fd) => {
console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
});
const { open } = require('node:fs');
const { executionAsyncId, executionAsyncResource } = require('node:async_hooks');
console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
open(__filename, 'r', (err, fd) => {
console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
});
これは、メタデータを格納するための追跡 `Map` を使用せずに、継続ローカルストレージを実装するために使用できます。
import { createServer } from 'node:http';
import {
executionAsyncId,
executionAsyncResource,
createHook,
} from 'async_hooks';
const sym = Symbol('state'); // Private symbol to avoid pollution
createHook({
init(asyncId, type, triggerAsyncId, resource) {
const cr = executionAsyncResource();
if (cr) {
resource[sym] = cr[sym];
}
},
}).enable();
const server = createServer((req, res) => {
executionAsyncResource()[sym] = { state: req.url };
setTimeout(function() {
res.end(JSON.stringify(executionAsyncResource()[sym]));
}, 100);
}).listen(3000);
const { createServer } = require('node:http');
const {
executionAsyncId,
executionAsyncResource,
createHook,
} = require('node:async_hooks');
const sym = Symbol('state'); // Private symbol to avoid pollution
createHook({
init(asyncId, type, triggerAsyncId, resource) {
const cr = executionAsyncResource();
if (cr) {
resource[sym] = cr[sym];
}
},
}).enable();
const server = createServer((req, res) => {
executionAsyncResource()[sym] = { state: req.url };
setTimeout(function() {
res.end(JSON.stringify(executionAsyncResource()[sym]));
}, 100);
}).listen(3000);
async_hooks.executionAsyncId()
#
- 戻り値: <number> 現在の`実行コンテキストの `asyncId`。何かが呼び出されたときを追跡するのに役立ちます。
import { executionAsyncId } from 'node:async_hooks';
import fs from 'node:fs';
console.log(executionAsyncId()); // 1 - bootstrap
const path = '.';
fs.open(path, 'r', (err, fd) => {
console.log(executionAsyncId()); // 6 - open()
});
const async_hooks = require('node:async_hooks');
const fs = require('node:fs');
console.log(async_hooks.executionAsyncId()); // 1 - bootstrap
const path = '.';
fs.open(path, 'r', (err, fd) => {
console.log(async_hooks.executionAsyncId()); // 6 - open()
});
`executionAsyncId()` から返される ID は、実行タイミングに関連しており、因果関係には関連していません(因果関係は `triggerAsyncId()` でカバーされています)。
const server = net.createServer((conn) => {
// Returns the ID of the server, not of the new connection, because the
// callback runs in the execution scope of the server's MakeCallback().
async_hooks.executionAsyncId();
}).listen(port, () => {
// Returns the ID of a TickObject (process.nextTick()) because all
// callbacks passed to .listen() are wrapped in a nextTick().
async_hooks.executionAsyncId();
});
Promise コンテキストは、デフォルトでは正確な `executionAsyncId` を取得できない場合があります。promise 実行追跡に関するセクションを参照してください。
async_hooks.triggerAsyncId()
#
- 戻り値: <number> 現在実行されているコールバックの呼び出しに関与しているリソースの ID。
const server = net.createServer((conn) => {
// The resource that caused (or triggered) this callback to be called
// was that of the new connection. Thus the return value of triggerAsyncId()
// is the asyncId of "conn".
async_hooks.triggerAsyncId();
}).listen(port, () => {
// Even though all callbacks passed to .listen() are wrapped in a nextTick()
// the callback itself exists because the call to the server's .listen()
// was made. So the return value would be the ID of the server.
async_hooks.triggerAsyncId();
});
Promise コンテキストは、デフォルトでは有効な `triggerAsyncId` を取得できない場合があります。promise 実行追跡に関するセクションを参照してください。
async_hooks.asyncWrapProviders
#
- 戻り値: プロバイダータイプと対応する数値 ID のマップ。このマップには、`async_hooks.init()` イベントによって発行される可能性のあるすべてのイベントタイプが含まれています。
この機能は、非推奨の `process.binding('async_wrap').Providers` の使用を抑制します。 DEP0111 を参照してください。
Promise 実行追跡#
デフォルトでは、V8 が提供する promiseイントロスペクション API が比較的負荷の高い性質を持っているため、promise の実行には `asyncId` が割り当てられません。これは、promise または `async`/`await` を使用するプログラムは、デフォルトでは promise コールバックコンテキストの正しい実行 ID とトリガー ID を取得できないことを意味します。
import { executionAsyncId, triggerAsyncId } from 'node:async_hooks';
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 1 tid 0
const { executionAsyncId, triggerAsyncId } = require('node:async_hooks');
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 1 tid 0
非同期ホップが含まれていたとしても、`then()` コールバックは外部スコープのコンテキストで実行されたと主張していることに注意してください。また、`triggerAsyncId` の値は `0` です。これは、`then()` コールバックの実行を発生させた(トリガーした)リソースに関するコンテキストが不足していることを意味します。
`async_hooks.createHook` を介して非同期フックをインストールすると、promise 実行追跡が有効になります。
import { createHook, executionAsyncId, triggerAsyncId } from 'node:async_hooks';
createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled.
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 7 tid 6
const { createHook, executionAsyncId, triggerAsyncId } = require('node:async_hooks');
createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled.
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 7 tid 6
この例では、実際のフック関数を追加することで、Promiseの追跡が可能になります。上記の例には、`Promise.resolve()` によって作成されたPromiseと、`then()` の呼び出しによって返されたPromiseの2つのPromiseがあります。上記の例では、最初のPromiseは `asyncId` `6` を取得し、後者は `asyncId` `7` を取得しました。 `then()` コールバックの実行中は、`asyncId` `7` を持つPromiseのコンテキストで実行しています。このPromiseは、非同期リソース `6` によってトリガーされました。
Promiseに関するもう1つの微妙な点は、`before` コールバックと `after` コールバックが、チェーンされたPromiseでのみ実行されることです。つまり、`then()`/`catch()` によって作成されていないPromiseでは、`before` コールバックと `after` コールバックは実行されません。詳細については、V8 の PromiseHooks API の詳細を参照してください。
JavaScript エンベダー API#
I/O、コネクションプーリング、コールバックキューの管理などのタスクを実行する独自の非同期リソースを処理するライブラリ開発者は、すべての適切なコールバックが呼び出されるように、`AsyncResource` JavaScript APIを使用できます。
クラス: `AsyncResource`#
このクラスのドキュメントは、`AsyncResource` に移動しました。
クラス: `AsyncLocalStorage`#
このクラスのドキュメントは、`AsyncLocalStorage` に移動しました。