Skip to content

Instantly share code, notes, and snippets.

@men232
Last active January 16, 2024 19:09
Show Gist options
  • Save men232/2a048c98fbeb5d6981f598b0a6a8917f to your computer and use it in GitHub Desktop.
Save men232/2a048c98fbeb5d6981f598b0a6a8917f to your computer and use it in GitHub Desktop.
NodeJs provide-inject (vue style)
import { AsyncLocalStorage } from 'node:async_hooks';
const ALS = new AsyncLocalStorage();
/**
* Execute provided function in async context.
* @param {function} fn
*/
export function withAppInject(fn) {
return ALS.run(new Map(), fn);
}
/**
* @typedef {symbol | string | number | object} ProvideKey
* @typedef {unknown} ProvideValue
*/
/**
* To provide data to a descendants
* @param {ProvideKey} key
* @param {ProvideValue} value
*/
export function provide(key, value) {
let store = ALS.getStore();
if (!store) {
console.warn(`provide() can only be used inside async context.\n` + captureStackTrace(provide));
return;
}
store.set(key, value);
}
/**
* @template [T = ProvideValue]
* @param {ProvideKey} key
* @return {T | undefined}
*/
export function inject(key) {
const store = ALS.getStore();
if (!store) {
console.warn(`inject() can only be used inside async context.\n` + captureStackTrace(inject));
return;
}
return store.get(key);
}
/**
* Returns true if `inject()` can be used without warning about being called in the wrong place.
* @return {boolean}
*/
export function hasInjectionContext() {
return !!ALS.getStore();
}
/**
* @param {function} till
* @return {string}
*/
function captureStackTrace(till) {
const err = new Error('');
Error.captureStackTrace(err, till);
return err.stack.slice(6);
}
import { withAppInject, provide, inject } from "./appinject.mjs";
for (let idx = 0; idx < 3; idx++) {
withAppInject(main).catch(console.error);
}
async function main() {
const requestId = Date.now() + '-' + Math.floor(Math.random() * 1000); // req.headers.get('x-request-id')
provide('requestId', requestId);
console.log('🏁 [start] req =', requestId);
logRequestId();
}
function logRequestId() {
// Here we will get the same request id (isoltaed) that generated in main function
const requestId = inject('requestId');
console.log('🛑 [end] req =', requestId);
}
import { randomUUID } from "node:crypto";
import { hasInjectionContext, inject, provide } from "./appinject.mjs";
/**
* Returns previous generated uuid in the same context or the new one
* @return {string}
*/
export function useTraceId() {
if (!hasInjectionContext()) {
console.warn(
new Error("useTraceId() can only be used inside async context.").stack
);
return randomUUID();
}
let traceId = inject("traceId");
if (traceId) {
return traceId;
}
traceId = randomUUID();
provide("traceId", traceId);
return traceId;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment