Function wrapper utilities for controlling execution patterns and providing no-op functionality in various programming contexts.
Wraps a function to ensure it can only execute once. The wrapped function does not return the result of the original function and subsequent calls do nothing.
/**
* Wraps a function to ensure it can only execute once
* @param method - The function to be wrapped
* @returns A wrapped function that executes the method once and returns undefined on all calls
*/
function once<T extends Function>(method: T): (...args: any[]) => void;Usage Examples:
import { once } from "@hapi/hoek";
// Basic usage
const myFunction = () => {
console.log('This will only run once');
return 'result';
};
const onceFunction = once(myFunction);
const result1 = onceFunction(); // Logs message, returns undefined
const result2 = onceFunction(); // Returns undefined, no log
const result3 = onceFunction(); // Returns undefined, no log
// Initialization function
const initializeApp = once(() => {
console.log('Initializing application...');
// Setup code here
return { initialized: true };
});
// Safe to call multiple times
initializeApp(); // Runs initialization, returns undefined
initializeApp(); // Does nothing, returns undefined
initializeApp(); // Does nothing, returns undefined
// Event handler that should only run once
const handleFirstClick = once((event: Event) => {
console.log('First click detected!');
// Setup one-time behavior
});
button.addEventListener('click', handleFirstClick);
// First click triggers the handler, subsequent clicks do nothing
// Async function wrapping
const fetchUserOnce = once(async (userId: string) => {
console.log(`Fetching user ${userId}...`);
const response = await fetch(`/api/users/${userId}`);
return response.json();
});
// Multiple calls, but only first one executes
const user1 = await fetchUserOnce('123'); // Fetches user, returns undefined
const user2 = await fetchUserOnce('123'); // Returns undefined
const user3 = await fetchUserOnce('456'); // Returns undefined (still same wrapped function)
// Constructor or factory function
const createSingleton = once(() => {
return {
id: Math.random(),
createdAt: new Date(),
// singleton properties
};
});
const singleton1 = createSingleton(); // Creates instance, returns undefined
const singleton2 = createSingleton(); // Returns undefined
// Note: The created instance is not returned, you need a different pattern for singletons
// Method wrapping with context preservation
class Logger {
private setupDone = false;
constructor() {
this.setup = once(this.setup.bind(this));
}
setup() {
console.log('Setting up logger...');
this.setupDone = true;
}
log(message: string) {
this.setup(); // Safe to call multiple times
if (this.setupDone) {
console.log(message);
}
}
}
// Cleanup or teardown functions
const cleanup = once(() => {
console.log('Cleaning up resources...');
// Cleanup code that should only run once
});
process.on('exit', cleanup);
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
// Cleanup will only run once regardless of which signal is receivedA reusable no-op function that accepts any arguments and returns undefined.
/**
* A reusable no-op function
* @param args - Any number of arguments (all ignored)
* @returns undefined
*/
function ignore(...args: any): void;Usage Examples:
import { ignore } from "@hapi/hoek";
// Default callback parameter
function processData(data: any[], callback = ignore) {
// Process data
for (const item of data) {
// Do processing
}
callback(null, data); // Safe to call even if no callback provided
}
// Usage without callback
processData([1, 2, 3]); // Works fine, ignore handles the callback
// Promise.catch() placeholder
Promise.resolve('success')
.then(result => console.log(result))
.catch(ignore); // Silently ignore errors
// Event handler placeholder
const button = document.getElementById('myButton');
button?.addEventListener('click', ignore); // No-op click handler
// Optional error handling
function riskyOperation(onError = ignore) {
try {
// Some risky operation
throw new Error('Something went wrong');
} catch (error) {
onError(error); // Safe to call even if onError is not provided
}
}
// Array methods with placeholder functions
const data = [1, 2, 3, 4, 5];
// Placeholder for unused array callback parameters
const processedData = data.map((value, index, array) => {
ignore(index, array); // Explicitly ignore unused parameters
return value * 2;
});
// Stream or event emitter placeholder
const stream = new EventEmitter();
stream.on('data', ignore); // Ignore data events
stream.on('error', ignore); // Ignore error events
// Testing or development placeholder
const api = {
development: {
log: console.log,
error: console.error
},
production: {
log: ignore, // Disable logging in production
error: console.error
}
};
const logger = process.env.NODE_ENV === 'production'
? api.production
: api.development;
logger.log('This may or may not appear');
// Conditional function execution
const debugMode = false;
const debugLog = debugMode ? console.log : ignore;
debugLog('Debug message'); // Only logs if debugMode is true
// Placeholder in function composition
const pipeline = [
processStep1,
processStep2,
debugMode ? logStep : ignore,
processStep3
];
// Clean way to handle optional operations
function setupApplication(onProgress = ignore, onComplete = ignore) {
onProgress('Starting setup...');
// Setup steps
onProgress('Installing dependencies...');
// More setup
onComplete('Setup completed');
}
// Can call with or without callbacks
setupApplication(); // Works fine
setupApplication(console.log); // With progress logging
setupApplication(console.log, () => console.log('Done!')); // With both callbacksImportant Notes:
once does NOT preserve the original function's return type - all calls return undefinedonce is thread-safe and handles concurrent calls correctlyignore is a constant function reference, making it efficient for repeated use