node/lib/internal/async_local_storage/async_context_frame.js

83 lines
1.7 KiB
JavaScript

'use strict';
const {
ObjectIs,
ReflectApply,
} = primordials;
const {
validateObject,
} = require('internal/validators');
const AsyncContextFrame = require('internal/async_context_frame');
const { AsyncResource } = require('async_hooks');
class AsyncLocalStorage {
#defaultValue = undefined;
#name = undefined;
/**
* @typedef {object} AsyncLocalStorageOptions
* @property {any} [defaultValue] - The default value to use when no value is set.
* @property {string} [name] - The name of the storage.
*/
/**
* @param {AsyncLocalStorageOptions} [options]
*/
constructor(options = {}) {
validateObject(options, 'options');
this.#defaultValue = options.defaultValue;
if (options.name !== undefined) {
this.#name = `${options.name}`;
}
}
/** @type {string} */
get name() { return this.#name || ''; }
static bind(fn) {
return AsyncResource.bind(fn);
}
static snapshot() {
return AsyncLocalStorage.bind((cb, ...args) => cb(...args));
}
disable() {
AsyncContextFrame.disable(this);
}
enterWith(data) {
const frame = new AsyncContextFrame(this, data);
AsyncContextFrame.set(frame);
}
run(data, fn, ...args) {
const prior = this.getStore();
if (ObjectIs(prior, data)) {
return ReflectApply(fn, null, args);
}
this.enterWith(data);
try {
return ReflectApply(fn, null, args);
} finally {
this.enterWith(prior);
}
}
exit(fn, ...args) {
return this.run(undefined, fn, ...args);
}
getStore() {
const frame = AsyncContextFrame.current();
if (!frame?.has(this)) {
return this.#defaultValue;
}
return frame?.get(this);
}
}
module.exports = AsyncLocalStorage;