1 | /**
|
2 | * The `async_hooks` module provides an API to track asynchronous resources. It
|
3 | * can be accessed using:
|
4 | *
|
5 | * ```js
|
6 | * import async_hooks from 'async_hooks';
|
7 | * ```
|
8 | * @experimental
|
9 | * @see [source](https://github.com/nodejs/node/blob/v16.6.0/lib/async_hooks.js)
|
10 | */
|
11 | declare module 'async_hooks' {
|
12 | /**
|
13 | * ```js
|
14 | * import { executionAsyncId } from 'async_hooks';
|
15 | *
|
16 | * console.log(executionAsyncId()); // 1 - bootstrap
|
17 | * fs.open(path, 'r', (err, fd) => {
|
18 | * console.log(executionAsyncId()); // 6 - open()
|
19 | * });
|
20 | * ```
|
21 | *
|
22 | * The ID returned from `executionAsyncId()` is related to execution timing, not
|
23 | * causality (which is covered by `triggerAsyncId()`):
|
24 | *
|
25 | * ```js
|
26 | * const server = net.createServer((conn) => {
|
27 | * // Returns the ID of the server, not of the new connection, because the
|
28 | * // callback runs in the execution scope of the server's MakeCallback().
|
29 | * async_hooks.executionAsyncId();
|
30 | *
|
31 | * }).listen(port, () => {
|
32 | * // Returns the ID of a TickObject (process.nextTick()) because all
|
33 | * // callbacks passed to .listen() are wrapped in a nextTick().
|
34 | * async_hooks.executionAsyncId();
|
35 | * });
|
36 | * ```
|
37 | *
|
38 | * Promise contexts may not get precise `executionAsyncIds` by default.
|
39 | * See the section on `promise execution tracking`.
|
40 | * @since v8.1.0
|
41 | * @return The `asyncId` of the current execution context. Useful to track when something calls.
|
42 | */
|
43 | function executionAsyncId(): number;
|
44 | /**
|
45 | * Resource objects returned by `executionAsyncResource()` are most often internal
|
46 | * Node.js handle objects with undocumented APIs. Using any functions or properties
|
47 | * on the object is likely to crash your application and should be avoided.
|
48 | *
|
49 | * Using `executionAsyncResource()` in the top-level execution context will
|
50 | * return an empty object as there is no handle or request object to use,
|
51 | * but having an object representing the top-level can be helpful.
|
52 | *
|
53 | * ```js
|
54 | * import { open } from 'fs';
|
55 | * import { executionAsyncId, executionAsyncResource } from 'async_hooks';
|
56 | *
|
57 | * console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
|
58 | * open(new URL(import.meta.url), 'r', (err, fd) => {
|
59 | * console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
|
60 | * });
|
61 | * ```
|
62 | *
|
63 | * This can be used to implement continuation local storage without the
|
64 | * use of a tracking `Map` to store the metadata:
|
65 | *
|
66 | * ```js
|
67 | * import { createServer } from 'http';
|
68 | * import {
|
69 | * executionAsyncId,
|
70 | * executionAsyncResource,
|
71 | * createHook
|
72 | * } from 'async_hooks';
|
73 | * const sym = Symbol('state'); // Private symbol to avoid pollution
|
74 | *
|
75 | * createHook({
|
76 | * init(asyncId, type, triggerAsyncId, resource) {
|
77 | * const cr = executionAsyncResource();
|
78 | * if (cr) {
|
79 | * resource[sym] = cr[sym];
|
80 | * }
|
81 | * }
|
82 | * }).enable();
|
83 | *
|
84 | * const server = createServer((req, res) => {
|
85 | * executionAsyncResource()[sym] = { state: req.url };
|
86 | * setTimeout(function() {
|
87 | * res.end(JSON.stringify(executionAsyncResource()[sym]));
|
88 | * }, 100);
|
89 | * }).listen(3000);
|
90 | * ```
|
91 | * @since v13.9.0, v12.17.0
|
92 | * @return The resource representing the current execution. Useful to store data within the resource.
|
93 | */
|
94 | function executionAsyncResource(): object;
|
95 | /**
|
96 | * ```js
|
97 | * const server = net.createServer((conn) => {
|
98 | * // The resource that caused (or triggered) this callback to be called
|
99 | * // was that of the new connection. Thus the return value of triggerAsyncId()
|
100 | * // is the asyncId of "conn".
|
101 | * async_hooks.triggerAsyncId();
|
102 | *
|
103 | * }).listen(port, () => {
|
104 | * // Even though all callbacks passed to .listen() are wrapped in a nextTick()
|
105 | * // the callback itself exists because the call to the server's .listen()
|
106 | * // was made. So the return value would be the ID of the server.
|
107 | * async_hooks.triggerAsyncId();
|
108 | * });
|
109 | * ```
|
110 | *
|
111 | * Promise contexts may not get valid `triggerAsyncId`s by default. See
|
112 | * the section on `promise execution tracking`.
|
113 | * @return The ID of the resource responsible for calling the callback that is currently being executed.
|
114 | */
|
115 | function triggerAsyncId(): number;
|
116 | interface HookCallbacks {
|
117 | /**
|
118 | * Called when a class is constructed that has the possibility to emit an asynchronous event.
|
119 | * @param asyncId a unique ID for the async resource
|
120 | * @param type the type of the async resource
|
121 | * @param triggerAsyncId the unique ID of the async resource in whose execution context this async resource was created
|
122 | * @param resource reference to the resource representing the async operation, needs to be released during destroy
|
123 | */
|
124 | init?(asyncId: number, type: string, triggerAsyncId: number, resource: object): void;
|
125 | /**
|
126 | * When an asynchronous operation is initiated or completes a callback is called to notify the user.
|
127 | * The before callback is called just before said callback is executed.
|
128 | * @param asyncId the unique identifier assigned to the resource about to execute the callback.
|
129 | */
|
130 | before?(asyncId: number): void;
|
131 | /**
|
132 | * Called immediately after the callback specified in before is completed.
|
133 | * @param asyncId the unique identifier assigned to the resource which has executed the callback.
|
134 | */
|
135 | after?(asyncId: number): void;
|
136 | /**
|
137 | * Called when a promise has resolve() called. This may not be in the same execution id
|
138 | * as the promise itself.
|
139 | * @param asyncId the unique id for the promise that was resolve()d.
|
140 | */
|
141 | promiseResolve?(asyncId: number): void;
|
142 | /**
|
143 | * Called after the resource corresponding to asyncId is destroyed
|
144 | * @param asyncId a unique ID for the async resource
|
145 | */
|
146 | destroy?(asyncId: number): void;
|
147 | }
|
148 | interface AsyncHook {
|
149 | /**
|
150 | * Enable the callbacks for a given AsyncHook instance. If no callbacks are provided enabling is a noop.
|
151 | */
|
152 | enable(): this;
|
153 | /**
|
154 | * Disable the callbacks for a given AsyncHook instance from the global pool of AsyncHook callbacks to be executed. Once a hook has been disabled it will not be called again until enabled.
|
155 | */
|
156 | disable(): this;
|
157 | }
|
158 | /**
|
159 | * Registers functions to be called for different lifetime events of each async
|
160 | * operation.
|
161 | *
|
162 | * The callbacks `init()`/`before()`/`after()`/`destroy()` are called for the
|
163 | * respective asynchronous event during a resource's lifetime.
|
164 | *
|
165 | * All callbacks are optional. For example, if only resource cleanup needs to
|
166 | * be tracked, then only the `destroy` callback needs to be passed. The
|
167 | * specifics of all functions that can be passed to `callbacks` is in the `Hook Callbacks` section.
|
168 | *
|
169 | * ```js
|
170 | * import { createHook } from 'async_hooks';
|
171 | *
|
172 | * const asyncHook = createHook({
|
173 | * init(asyncId, type, triggerAsyncId, resource) { },
|
174 | * destroy(asyncId) { }
|
175 | * });
|
176 | * ```
|
177 | *
|
178 | * The callbacks will be inherited via the prototype chain:
|
179 | *
|
180 | * ```js
|
181 | * class MyAsyncCallbacks {
|
182 | * init(asyncId, type, triggerAsyncId, resource) { }
|
183 | * destroy(asyncId) {}
|
184 | * }
|
185 | *
|
186 | * class MyAddedCallbacks extends MyAsyncCallbacks {
|
187 | * before(asyncId) { }
|
188 | * after(asyncId) { }
|
189 | * }
|
190 | *
|
191 | * const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
|
192 | * ```
|
193 | *
|
194 | * Because promises are asynchronous resources whose lifecycle is tracked
|
195 | * via the async hooks mechanism, the `init()`, `before()`, `after()`, and`destroy()` callbacks _must not_ be async functions that return promises.
|
196 | * @since v8.1.0
|
197 | * @param callbacks The `Hook Callbacks` to register
|
198 | * @return Instance used for disabling and enabling hooks
|
199 | */
|
200 | function createHook(callbacks: HookCallbacks): AsyncHook;
|
201 | interface AsyncResourceOptions {
|
202 | /**
|
203 | * The ID of the execution context that created this async event.
|
204 | * @default executionAsyncId()
|
205 | */
|
206 | triggerAsyncId?: number | undefined;
|
207 | /**
|
208 | * Disables automatic `emitDestroy` when the object is garbage collected.
|
209 | * This usually does not need to be set (even if `emitDestroy` is called
|
210 | * manually), unless the resource's `asyncId` is retrieved and the
|
211 | * sensitive API's `emitDestroy` is called with it.
|
212 | * @default false
|
213 | */
|
214 | requireManualDestroy?: boolean | undefined;
|
215 | }
|
216 | /**
|
217 | * The class `AsyncResource` is designed to be extended by the embedder's async
|
218 | * resources. Using this, users can easily trigger the lifetime events of their
|
219 | * own resources.
|
220 | *
|
221 | * The `init` hook will trigger when an `AsyncResource` is instantiated.
|
222 | *
|
223 | * The following is an overview of the `AsyncResource` API.
|
224 | *
|
225 | * ```js
|
226 | * import { AsyncResource, executionAsyncId } from 'async_hooks';
|
227 | *
|
228 | * // AsyncResource() is meant to be extended. Instantiating a
|
229 | * // new AsyncResource() also triggers init. If triggerAsyncId is omitted then
|
230 | * // async_hook.executionAsyncId() is used.
|
231 | * const asyncResource = new AsyncResource(
|
232 | * type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false }
|
233 | * );
|
234 | *
|
235 | * // Run a function in the execution context of the resource. This will
|
236 | * // * establish the context of the resource
|
237 | * // * trigger the AsyncHooks before callbacks
|
238 | * // * call the provided function `fn` with the supplied arguments
|
239 | * // * trigger the AsyncHooks after callbacks
|
240 | * // * restore the original execution context
|
241 | * asyncResource.runInAsyncScope(fn, thisArg, ...args);
|
242 | *
|
243 | * // Call AsyncHooks destroy callbacks.
|
244 | * asyncResource.emitDestroy();
|
245 | *
|
246 | * // Return the unique ID assigned to the AsyncResource instance.
|
247 | * asyncResource.asyncId();
|
248 | *
|
249 | * // Return the trigger ID for the AsyncResource instance.
|
250 | * asyncResource.triggerAsyncId();
|
251 | * ```
|
252 | */
|
253 | class AsyncResource {
|
254 | /**
|
255 | * AsyncResource() is meant to be extended. Instantiating a
|
256 | * new AsyncResource() also triggers init. If triggerAsyncId is omitted then
|
257 | * async_hook.executionAsyncId() is used.
|
258 | * @param type The type of async event.
|
259 | * @param triggerAsyncId The ID of the execution context that created
|
260 | * this async event (default: `executionAsyncId()`), or an
|
261 | * AsyncResourceOptions object (since 9.3)
|
262 | */
|
263 | constructor(type: string, triggerAsyncId?: number | AsyncResourceOptions);
|
264 | /**
|
265 | * Binds the given function to the current execution context.
|
266 | *
|
267 | * The returned function will have an `asyncResource` property referencing
|
268 | * the `AsyncResource` to which the function is bound.
|
269 | * @since v14.8.0, v12.19.0
|
270 | * @param fn The function to bind to the current execution context.
|
271 | * @param type An optional name to associate with the underlying `AsyncResource`.
|
272 | */
|
273 | static bind<Func extends (this: ThisArg, ...args: any[]) => any, ThisArg>(
|
274 | fn: Func,
|
275 | type?: string,
|
276 | thisArg?: ThisArg
|
277 | ): Func & {
|
278 | asyncResource: AsyncResource;
|
279 | };
|
280 | /**
|
281 | * Binds the given function to execute to this `AsyncResource`'s scope.
|
282 | *
|
283 | * The returned function will have an `asyncResource` property referencing
|
284 | * the `AsyncResource` to which the function is bound.
|
285 | * @since v14.8.0, v12.19.0
|
286 | * @param fn The function to bind to the current `AsyncResource`.
|
287 | */
|
288 | bind<Func extends (...args: any[]) => any>(
|
289 | fn: Func
|
290 | ): Func & {
|
291 | asyncResource: AsyncResource;
|
292 | };
|
293 | /**
|
294 | * Call the provided function with the provided arguments in the execution context
|
295 | * of the async resource. This will establish the context, trigger the AsyncHooks
|
296 | * before callbacks, call the function, trigger the AsyncHooks after callbacks, and
|
297 | * then restore the original execution context.
|
298 | * @since v9.6.0
|
299 | * @param fn The function to call in the execution context of this async resource.
|
300 | * @param thisArg The receiver to be used for the function call.
|
301 | * @param args Optional arguments to pass to the function.
|
302 | */
|
303 | runInAsyncScope<This, Result>(fn: (this: This, ...args: any[]) => Result, thisArg?: This, ...args: any[]): Result;
|
304 | /**
|
305 | * Call all `destroy` hooks. This should only ever be called once. An error will
|
306 | * be thrown if it is called more than once. This **must** be manually called. If
|
307 | * the resource is left to be collected by the GC then the `destroy` hooks will
|
308 | * never be called.
|
309 | * @return A reference to `asyncResource`.
|
310 | */
|
311 | emitDestroy(): this;
|
312 | /**
|
313 | * @return The unique `asyncId` assigned to the resource.
|
314 | */
|
315 | asyncId(): number;
|
316 | /**
|
317 | *
|
318 | * @return The same `triggerAsyncId` that is passed to the `AsyncResource` constructor.
|
319 | */
|
320 | triggerAsyncId(): number;
|
321 | }
|
322 | /**
|
323 | * This class creates stores that stay coherent through asynchronous operations.
|
324 | *
|
325 | * While you can create your own implementation on top of the `async_hooks` module,`AsyncLocalStorage` should be preferred as it is a performant and memory safe
|
326 | * implementation that involves significant optimizations that are non-obvious to
|
327 | * implement.
|
328 | *
|
329 | * The following example uses `AsyncLocalStorage` to build a simple logger
|
330 | * that assigns IDs to incoming HTTP requests and includes them in messages
|
331 | * logged within each request.
|
332 | *
|
333 | * ```js
|
334 | * import http from 'http';
|
335 | * import { AsyncLocalStorage } from 'async_hooks';
|
336 | *
|
337 | * const asyncLocalStorage = new AsyncLocalStorage();
|
338 | *
|
339 | * function logWithId(msg) {
|
340 | * const id = asyncLocalStorage.getStore();
|
341 | * console.log(`${id !== undefined ? id : '-'}:`, msg);
|
342 | * }
|
343 | *
|
344 | * let idSeq = 0;
|
345 | * http.createServer((req, res) => {
|
346 | * asyncLocalStorage.run(idSeq++, () => {
|
347 | * logWithId('start');
|
348 | * // Imagine any chain of async operations here
|
349 | * setImmediate(() => {
|
350 | * logWithId('finish');
|
351 | * res.end();
|
352 | * });
|
353 | * });
|
354 | * }).listen(8080);
|
355 | *
|
356 | * http.get('http://localhost:8080');
|
357 | * http.get('http://localhost:8080');
|
358 | * // Prints:
|
359 | * // 0: start
|
360 | * // 1: start
|
361 | * // 0: finish
|
362 | * // 1: finish
|
363 | * ```
|
364 | *
|
365 | * Each instance of `AsyncLocalStorage` maintains an independent storage context.
|
366 | * Multiple instances can safely exist simultaneously without risk of interfering
|
367 | * with each other data.
|
368 | * @since v13.10.0, v12.17.0
|
369 | */
|
370 | class AsyncLocalStorage<T> {
|
371 | /**
|
372 | * Disables the instance of `AsyncLocalStorage`. All subsequent calls
|
373 | * to `asyncLocalStorage.getStore()` will return `undefined` until`asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()` is called again.
|
374 | *
|
375 | * When calling `asyncLocalStorage.disable()`, all current contexts linked to the
|
376 | * instance will be exited.
|
377 | *
|
378 | * Calling `asyncLocalStorage.disable()` is required before the`asyncLocalStorage` can be garbage collected. This does not apply to stores
|
379 | * provided by the `asyncLocalStorage`, as those objects are garbage collected
|
380 | * along with the corresponding async resources.
|
381 | *
|
382 | * Use this method when the `asyncLocalStorage` is not in use anymore
|
383 | * in the current process.
|
384 | * @since v13.10.0, v12.17.0
|
385 | * @experimental
|
386 | */
|
387 | disable(): void;
|
388 | /**
|
389 | * Returns the current store.
|
390 | * If called outside of an asynchronous context initialized by
|
391 | * calling `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()`, it
|
392 | * returns `undefined`.
|
393 | * @since v13.10.0, v12.17.0
|
394 | */
|
395 | getStore(): T | undefined;
|
396 | /**
|
397 | * Runs a function synchronously within a context and returns its
|
398 | * return value. The store is not accessible outside of the callback function or
|
399 | * the asynchronous operations created within the callback.
|
400 | *
|
401 | * The optional `args` are passed to the callback function.
|
402 | *
|
403 | * If the callback function throws an error, the error is thrown by `run()` too.
|
404 | * The stacktrace is not impacted by this call and the context is exited.
|
405 | *
|
406 | * Example:
|
407 | *
|
408 | * ```js
|
409 | * const store = { id: 2 };
|
410 | * try {
|
411 | * asyncLocalStorage.run(store, () => {
|
412 | * asyncLocalStorage.getStore(); // Returns the store object
|
413 | * throw new Error();
|
414 | * });
|
415 | * } catch (e) {
|
416 | * asyncLocalStorage.getStore(); // Returns undefined
|
417 | * // The error will be caught here
|
418 | * }
|
419 | * ```
|
420 | * @since v13.10.0, v12.17.0
|
421 | */
|
422 | run<R, TArgs extends any[]>(store: T, callback: (...args: TArgs) => R, ...args: TArgs): R;
|
423 | /**
|
424 | * Runs a function synchronously outside of a context and returns its
|
425 | * return value. The store is not accessible within the callback function or
|
426 | * the asynchronous operations created within the callback. Any `getStore()`call done within the callback function will always return `undefined`.
|
427 | *
|
428 | * The optional `args` are passed to the callback function.
|
429 | *
|
430 | * If the callback function throws an error, the error is thrown by `exit()` too.
|
431 | * The stacktrace is not impacted by this call and the context is re-entered.
|
432 | *
|
433 | * Example:
|
434 | *
|
435 | * ```js
|
436 | * // Within a call to run
|
437 | * try {
|
438 | * asyncLocalStorage.getStore(); // Returns the store object or value
|
439 | * asyncLocalStorage.exit(() => {
|
440 | * asyncLocalStorage.getStore(); // Returns undefined
|
441 | * throw new Error();
|
442 | * });
|
443 | * } catch (e) {
|
444 | * asyncLocalStorage.getStore(); // Returns the same object or value
|
445 | * // The error will be caught here
|
446 | * }
|
447 | * ```
|
448 | * @since v13.10.0, v12.17.0
|
449 | * @experimental
|
450 | */
|
451 | exit<R, TArgs extends any[]>(callback: (...args: TArgs) => R, ...args: TArgs): R;
|
452 | /**
|
453 | * Transitions into the context for the remainder of the current
|
454 | * synchronous execution and then persists the store through any following
|
455 | * asynchronous calls.
|
456 | *
|
457 | * Example:
|
458 | *
|
459 | * ```js
|
460 | * const store = { id: 1 };
|
461 | * // Replaces previous store with the given store object
|
462 | * asyncLocalStorage.enterWith(store);
|
463 | * asyncLocalStorage.getStore(); // Returns the store object
|
464 | * someAsyncOperation(() => {
|
465 | * asyncLocalStorage.getStore(); // Returns the same object
|
466 | * });
|
467 | * ```
|
468 | *
|
469 | * This transition will continue for the _entire_ synchronous execution.
|
470 | * This means that if, for example, the context is entered within an event
|
471 | * handler subsequent event handlers will also run within that context unless
|
472 | * specifically bound to another context with an `AsyncResource`. That is why`run()` should be preferred over `enterWith()` unless there are strong reasons
|
473 | * to use the latter method.
|
474 | *
|
475 | * ```js
|
476 | * const store = { id: 1 };
|
477 | *
|
478 | * emitter.on('my-event', () => {
|
479 | * asyncLocalStorage.enterWith(store);
|
480 | * });
|
481 | * emitter.on('my-event', () => {
|
482 | * asyncLocalStorage.getStore(); // Returns the same object
|
483 | * });
|
484 | *
|
485 | * asyncLocalStorage.getStore(); // Returns undefined
|
486 | * emitter.emit('my-event');
|
487 | * asyncLocalStorage.getStore(); // Returns the same object
|
488 | * ```
|
489 | * @since v13.11.0, v12.17.0
|
490 | * @experimental
|
491 | */
|
492 | enterWith(store: T): void;
|
493 | }
|
494 | }
|
495 | declare module 'node:async_hooks' {
|
496 | export * from 'async_hooks';
|
497 | }
|
498 |
|
\ | No newline at end of file |