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)。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。