init(asyncId, type, triggerAsyncId, resource)
asyncId
<number> 异步资源的唯一 ID。type
<string> 异步资源的类型。triggerAsyncId
<number> 在其执行上下文中创建此异步资源的异步资源的唯一 ID。resource
<Object>对代表异步操作的资源的引用,需要在执行期间释放破坏.
在构造有可能发出异步事件的类时调用。这并不意味着实例必须在调用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
的资源类型。通常,它将对应于资源的构造函数的名称。
有效值为:
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, Microtask, Timeout, Immediate, TickObject
这些值可以在任何 Node.js 版本中更改。此外,
的用户可能会提供其他值。AsyncResource
还有PROMISE
资源类型,用于跟踪Promise
实例和它们调度的异步工作。
使用公共嵌入器 API 时,用户可以定义自己的type
。
可能存在类型名称冲突。鼓励嵌入器使用唯一的前缀,例如 npm 包名称,以防止在侦听钩子时发生冲突。
triggerAsyncId
#
triggerAsyncId
是导致(或 "triggered")新资源初始化并导致 init
调用的资源的 asyncId
。这与仅显示资源创建时间的async_hooks.executionAsyncId()
不同,而triggerAsyncId
显示创建资源的原因。
以下是triggerAsyncId
的简单演示:
import { createHook, executionAsyncId } from 'node:async_hooks'; import { stdout } from 'node:process'; import net from 'node:net'; 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'); 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 堆栈之外。 (0
的 executionAsyncId()
表示它是从 C++ 执行的,上面没有 JavaScript 堆栈。)仅凭这些信息,就不可能将资源链接在一起,因为它们是什么导致它们被创建,所以 triggerAsyncId
的任务是传播哪些资源负责新资源的存在。
resource
#
resource
是一个对象,表示已初始化的实际异步资源。这可以包含有用的信息,这些信息会根据 type
的值而变化。例如,对于 GETADDRINFOREQWRAP
资源类型,resource
提供在 net.Server.listen()
中查找主机 IP 地址时使用的主机名。不支持访问此信息的 API,但使用 Embedder API,用户可以提供和记录自己的资源对象。例如,这样的资源对象可能包含正在执行的 SQL 查询。
在某些情况下,出于性能原因会重用资源对象,因此将其用作WeakMap
中的键或向其添加属性是不安全的。
异步上下文示例#
下面是一个示例,其中包含有关在 before
和 after
调用之间对 init
的调用的附加信息,特别是对 listen()
的回调将是什么样子。输出格式稍微复杂一些,使调用上下文更容易看到。
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()
回调的'parent' 的原因。
该图仅显示创建资源的时间,而不显示创建原因,因此要跟踪使用 triggerAsyncId
的原因。可以用下图表示:
bootstrap(1)
|
˅
TCPSERVERWRAP(5)
|
˅
TickObject(6)
|
˅
Timeout(7)
相关用法
- Node.js indexOf()用法及代码示例
- Node.js inspector.Session.post(method[, params][, callback])用法及代码示例
- Node.js inspector.console用法及代码示例
- Node.js import.meta.resolve(specifier[, parent])用法及代码示例
- Node.js import.meta.url用法及代码示例
- Node.js ServerHttp2Stream http2stream.pushStream(headers[, options], callback)用法及代码示例
- Node.js http2.Http2ServerRequest request.url用法及代码示例
- Node.js request.socket用法及代码示例
- Node.js assert.notEqual(actual, expected[, message])用法及代码示例
- Node.js tlsSocket.authorized用法及代码示例
- Node.js zlib.deflateRaw()用法及代码示例
- Node.js http.IncomingMessage message.rawHeaders用法及代码示例
- Node.js Console用法及代码示例
- Node.js GM transparent()用法及代码示例
- Node.js URL.protocol用法及代码示例
- Node.js http.Agent.reuseSocket(socket, request)用法及代码示例
- Node.js fs.filehandle.datasync()用法及代码示例
- Node.js socket.bind()用法及代码示例
- Node.js v8.getHeapSpaceStatistics()用法及代码示例
- Node.js http2session.destroyed用法及代码示例
- Node.js http.ServerResponse response.statusCode用法及代码示例
- Node.js Buffer buf.writeBigUInt64BE(value[, offset])用法及代码示例
- Node.js Http2ServerResponse.finished用法及代码示例
- Node.js Http2Stream close用法及代码示例
- Node.js readStream.isRaw用法及代码示例
注:本文由纯净天空筛选整理自nodejs.org大神的英文原创作品 init(asyncId, type, triggerAsyncId, resource)。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。