(*) Stability: 1 - Experimental

Czym jest async_hooks?

 

Jest mechanizmem pozwalającym na śledzenie "życia" zasobów asynchronicznych. 

 

Zasoby asynchroniczne reprezentują obiekty z powiązanym wywołaniem.

Krótka historia

  • Pierwsza implementacja nosiła nazwę AsyncListener i została zaimplementowana w wersji Nodejs 0.11, ale została usunięta w wersji 0.12
  • Kolejna implementacja pojawiła się pod nazwą AsyncWrap(async_wrap) i została dodana do Nodejs w wersji 0.12.
    async_wrap był nieoficjalnym i niedokumentowanym dodatkiem w Nodejs w wersjach < 8.
  • async_hooks jest najnowszą i oficjalną paczką pozwalającą na podpięcie hook'ów do asynchronicznych wywołań. Implementacja pojawiła się w Nodejs w wersji 8.

Api

import async_hooks from 'node:async_hooks';

// Return the ID of the current execution context.
async_hooks.executionAsyncId();

// Return the ID of the handle responsible for triggering the callback 
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. 
asyncHook.enable();

// Disable listening for new asynchronous events.
asyncHook.disable();

createHook

import async_hooks from 'node:async_hooks';

async_hooks.createHook({
    init(asyncId, type, triggerAsyncId, resource) {
        // called when an async resource is initialized.
    },
    before(asyncId) {
        // before() is called just before the resource's callback is called.
    },
    after(asyncId) {
        // after() is called just after the resource's callback has finished.
    },
    destroy(asyncId) {
        // destroy() is called when the resource is destroyed.
    },
    promiseResolve(asyncId) {
        // called when the resolve function of the Promise constructor is invoked
    }
}).enable();

Obsługiwane typy w async_hooks

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, Promise

Uwaga!

Ponieważ logowanie do konsoli jest operacją asynchroniczną, console.log() spowoduje wywołanie callback'ów AsyncHook. Używanie console.log() lub podobnych operacji asynchronicznych wewnątrz funkcji AsyncHook spowoduje nieskończoną rekurencję.

// Przykład ominięcia petli i wyświetlenia logu w konsoli
import { writeFileSync } from 'node:fs';
import { format } from 'node:util';
writeFileSync(1, `${format(...args)}\n`);

// lub
process._rawDebug('some log');

init

Promise.resolve()
	.then(🎈() => {
  		return void 0;
	});

before

Promise.resolve()
	.then(() => 🎈{
  		return void 0;
	});

after

Promise.resolve()
	.then(() => {
  		return void 0;
	}🎈);

destroy

Promise.resolve()
	.then(() => {
  		return void 0;
	});
// ...
🎈

promiseResolve

Promise.resolve()
	.then(() => {
  		return void 0;🎈
	});

Demo

Wbudowana klasa modułu async_hooks pozwalająca w optymalny sposób korzystać z wyizolowanego stanu.

AsyncLocalStorage

Api

import { AsyncLocalStorage } from 'node:async_hooks';

const store = {};

// Creates a new instance of AsyncLocalStorage.
const asyncLocalStorage = new AsyncLocalStorage();

/// Runs a function synchronously within 
// a context and returns its return value.
asyncLocalStorage.run(store, () => {});

// Returns the current store
asyncLocalStorage.getStore();

// // Replaces previous store with the given store object
asyncLocalStorage.enterWith(store);

// Disables the instance of AsyncLocalStorage
asyncLocalStorage.disable();

// Runs a function synchronously outside of a context and returns its return value. 
asyncLocalStorage.exit(() => {
	asyncLocalStorage.getStore(); // Returns undefined
	throw new Error();
});

Demo

Przykazanie XI

"Coś za coś"

A co na to przeglądarka?

Przypadki użycia

  • Pomiary wydajności (profiling)
  • Oddzielny globalny kontekst dla każdego nowego zapytania (execution context)(uproszczone zbieranie logów, wspólny kontekst użytkownika, itp.)
  • Zaawansowane śledzenie przepływu

Dziękuję za uwagę!

  • https://nodejs.org/api/async_hooks.html
  • https://medium.com/nmc-techblog/the-power-of-async-hooks-in-node-js-8a2a84238acb
  • https://itnext.io/a-pragmatic-overview-of-async-hooks-api-in-node-js-e514b31460e9
  • https://blog.scottlogic.com/2019/03/04/lambda-global-state.html
Made with Slides.com