UNPKG

@midwayjs/async-hooks-context-manager

Version:

midway async hooks context manager

140 lines 4.8 kB
"use strict"; /* * Copyright The OpenTelemetry Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AsyncHooksContextManager = void 0; const core_1 = require("@midwayjs/core"); const asyncHooks = require("async_hooks"); class AsyncHooksContextManager { constructor() { this._contexts = new Map(); this._stack = []; this._asyncHook = asyncHooks.createHook({ init: this._init.bind(this), before: this._before.bind(this), after: this._after.bind(this), destroy: this._destroy.bind(this), promiseResolve: this._destroy.bind(this), }); } active() { var _a; return (_a = this._stack[this._stack.length - 1]) !== null && _a !== void 0 ? _a : core_1.ASYNC_ROOT_CONTEXT; } with(context, fn, thisArg, ...args) { this._enterContext(context); try { return fn.call(thisArg, ...args); } finally { this._exitContext(); } } enable() { this._asyncHook.enable(); return this; } disable() { this._asyncHook.disable(); this._contexts.clear(); this._stack = []; return this; } /** * Init hook will be called when userland create a async context, setting the * context as the current one if it exist. * @param uid id of the async context * @param type the resource type */ _init(uid, type) { // ignore TIMERWRAP as they combine timers with same timeout which can lead to // false context propagation. TIMERWRAP has been removed in node 11 // every timer has it's own `Timeout` resource anyway which is used to propagete // context. if (type === 'TIMERWRAP') return; const context = this._stack[this._stack.length - 1]; if (context !== undefined) { this._contexts.set(uid, context); } } /** * Destroy hook will be called when a given context is no longer used so we can * remove its attached context. * @param uid uid of the async context */ _destroy(uid) { this._contexts.delete(uid); } /** * Before hook is called just before executing a async context. * @param uid uid of the async context */ _before(uid) { const context = this._contexts.get(uid); if (context !== undefined) { this._enterContext(context); } } /** * After hook is called just after completing the execution of a async context. */ _after() { this._exitContext(); } /** * Set the given context as active */ _enterContext(context) { this._stack.push(context); } /** * Remove the context at the root of the stack */ _exitContext() { this._stack.pop(); } /** * Binds a the certain context or the active one to the target function and then returns the target * @param context A context (span) to be bind to target * @param target a function. When target or one of its callbacks is called, * the provided context will be used as the active context for the duration of the call. */ bind(context, target) { if (typeof target === 'function') { // eslint-disable-next-line @typescript-eslint/no-this-alias const manager = this; const contextWrapper = function (...args) { return manager.with(context, () => target.apply(this, args)); }; Object.defineProperty(contextWrapper, 'length', { enumerable: false, configurable: true, writable: false, value: target.length, }); /** * It isn't possible to tell Typescript that contextWrapper is the same as T * so we forced to cast as any here. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any return contextWrapper; } return target; } } exports.AsyncHooksContextManager = AsyncHooksContextManager; //# sourceMappingURL=asyncHooksContextManager.js.map