Wrappy
Wrappy is a tiny, foundational utility for reliably wrapping callback functions in JavaScript. Its primary function is to ensure that a given callback is invoked at most once, which is crucial for preventing unexpected behavior in asynchronous operations where a callback might inadvertently be called multiple times. Currently stable at version 1.0.2, released in 2015, the package maintains an extremely low release cadence due to its minimal scope and high stability, with updates typically addressing only critical edge cases or compatibility. Wrappy is largely an internal dependency for many other npm packages (e.g., `once`, `inflight`) rather than a direct application-level utility, providing a robust and dependency-free solution for basic callback management.
Common errors
-
TypeError: (0 , wrappy__WEBPACK_IMPORTED_MODULE_0__.wrappy) is not a function
cause This error typically occurs in bundled ES Module (ESM) contexts (e.g., Webpack, Rollup) when `wrappy` is incorrectly imported using a named import syntax (`import { wrappy } from 'wrappy'`) instead of a default import.fixChange the import statement from `import { wrappy } from 'wrappy'` to `import wrappy from 'wrappy'`. -
TypeError: wrappy is not a function
cause This can occur if `wrappy` is imported incorrectly in a CommonJS module, or if the imported `wrappy` variable is not the function itself but perhaps an object, or if it's being called as a method on an undefined object. It often signifies a mismatch in how the module's export is consumed.fixEnsure you are using `const wrappy = require('wrappy');` in CommonJS and calling `wrappy(callback)` directly. Verify that the `wrappy` variable holds the function reference before attempting to call it.
Warnings
- gotcha Wrappy is a low-level utility primarily designed for internal use by library authors, especially for managing asynchronous control flow in older CommonJS patterns. For direct application development, modern alternatives like Promises, async/await, or dedicated event emitters often provide more idiomatic and robust solutions for managing asynchronous operations and ensuring single execution.
- gotcha The `wrappy` package is a CommonJS module. When used in an ES Module (ESM) environment (e.g., with `type: "module"` in `package.json` or in a browser build process like Webpack/Rollup), it must be imported as a default export (`import wrappy from 'wrappy'`). Attempting a named import (`import { wrappy } from 'wrappy'`) will result in runtime errors as the named export `wrappy` does not exist.
- gotcha Wrappy explicitly guarantees a function is called *at most once*. It does not provide features like retry mechanisms, advanced error handling (beyond passing the error), debouncing, throttling, or complex flow control (e.g., parallel execution, sequencing tasks). Developers needing these more elaborate features should explore other specialized libraries or implement them using Promises.
Install
-
npm install wrappy -
yarn add wrappy -
pnpm add wrappy
Imports
- wrappy
const wrappy = require('wrappy') - wrappy
import { wrappy } from 'wrappy'import wrappy from 'wrappy'
- Callback Wrapper
const onceWrapper = wrappy.wrappy(callbackFn)
const onceWrapper = wrappy(callbackFn)
Quickstart
const wrappy = require('wrappy');
// Define a callback function that we want to ensure runs only once
let executionCount = 0;
function myCallback(err, data) {
if (err) {
console.error('Error:', err);
return;
}
executionCount++;
console.log(`Callback executed! Data: ${data}. Total executions: ${executionCount}`);
}
// Wrap the callback using wrappy
const wrappedCallback = wrappy(myCallback);
// Simulate an asynchronous operation where the callback might be called multiple times
console.log('--- Starting async operation simulation ---');
setTimeout(() => {
console.log('Attempting to call wrappedCallback (first time)');
wrappedCallback(null, 'Initial Data');
}, 100);
setTimeout(() => {
console.log('Attempting to call wrappedCallback (second time - should be ignored)');
wrappedCallback(null, 'More Data'); // This call should be ignored by wrappy
}, 200);
setTimeout(() => {
console.log('Attempting to call wrappedCallback (third time - should be ignored)');
wrappedCallback(null, 'Even More Data'); // This call should also be ignored
}, 300);
// Example of wrappy being used for error handling that only triggers once
function saveUserData(user, callback) {
const done = wrappy(callback);
let dbOperationComplete = false;
let fileWriteComplete = false;
// Simulate two async operations that need to complete, but only one `done` call
setTimeout(() => {
if (Math.random() > 0.9) return done(new Error('DB connection failed'));
dbOperationComplete = true;
console.log('DB operation finished.');
if (fileWriteComplete) done(null, 'User data saved successfully');
}, 150);
setTimeout(() => {
if (Math.random() > 0.9) return done(new Error('File write failed'));
fileWriteComplete = true;
console.log('File write finished.');
if (dbOperationComplete) done(null, 'User data saved successfully');
}, 250);
}
saveUserData({ id: 1, name: 'Alice' }, (err, result) => {
if (err) {
console.error('Save user data failed:', err.message);
return;
}
console.log('Save user data success:', result);
});