promise-breaker review
promise-breaker 7.0.0 adapts one function so callers can use either a Node-style error-first callback or a Promise. `promisify` starts from callback code, `callbackify` starts from Promise code, and `call` or `apply` invoke a plugin whose completion style is unknown. The current major is an ESM package and keeps lower-level `addPromise` and `addCallback` helpers for signatures that automatic callback detection cannot read safely. Our install loaded through both ESM import and CommonJS require, but it contained no TypeScript declarations despite the repository's TypeScript-oriented examples.
Our promise-breaker 7.0.0 install took 0.8 seconds and bundled to 0.7 KB gzipped, but it shipped no TypeScript declarations and dual completion still needs two-path tests. Add it for a real callback compatibility promise; codebases that control their callers should expose async functions directly.
We installed it
| Install | ✓ · 0.8s | 1 package on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · ESM package |
| Browser | 0.7 KB | gzipped (1.5 KB minified), bundled with esbuild |
| Types | — | no TypeScript types found |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does promise-breaker install cleanly?
Yes. In a fresh container with an empty cache, npm install promise-breaker finished in 0.8s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does promise-breaker add to a browser bundle?
0.7 KB gzipped (1.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does promise-breaker work with both ESM and CommonJS?
Yes. Both import 'promise-breaker' and require('promise-breaker') worked in Node 22 in our run. The package is published as ESM.
Does promise-breaker include TypeScript types?
No type declarations were found in our install, so TypeScript users need their own declarations.
promise-breaker or pify: which should you use?
pify: Use it when conversion only runs from callback functions toward Promise-returning functions. Our promise-breaker 7.0.0 install took 0.8 seconds and bundled to 0.7 KB gzipped, but it shipped no TypeScript declarations and dual completion still needs two-path tests.
When should you not use promise-breaker?
You own every caller and can choose async functions; a dual completion contract adds branches with no compatibility payoff
Use it if
- A published library must preserve callback callers while adding Promise calls
- A legacy codebase will migrate function by function instead of changing every caller together
- A plugin hook may return a Promise or call a Node-style callback, and the host has to accept both
- An unusual signature needs explicit `addPromise` or `addCallback` control rather than handwritten adapter boilerplate
- You own every caller and can choose async functions; a dual completion contract adds branches with no compatibility payoff
- Strict TypeScript requires package declarations; our 7.0.0 install found none
- The last real argument is itself a function or the signature is variadic; `callbackify` can mistake that function for the completion callback unless `args` is set
- You need cancellation, timeouts, progress events, several success values, or a callback convention other than `(error, result)`; these adapters add none of them
- A plugin may both call its callback and return a Promise; the unknown-style helpers cannot make double completion a safe contract
Setup reality
Our install of promise-breaker 7.0.0 finished in 0.8 seconds. It left 1 package using 1 MB on disk; the package itself was 64 KB unpacked with 0 direct dependencies and 0 peer dependencies. npm audit reported 0 known vulnerabilities. Package metadata marks it as ESM and provides no exports map, yet both ESM import and CommonJS require() worked in our Node 22 sandbox. No TypeScript declarations were found. The browser result was 1.5 KB minified and 0.7 KB gzipped.
promisify always appends an internal callback. When the caller already supplies a callback at the implementation's expected position, that caller callback handles completion and the extra callback is ignored. The wrapper still creates a Promise in that branch, and the README warns that it never settles. Callback callers must ignore the return value; do not feed it into Promise tracking or request-lifecycle code.
callbackify treats the last function argument as the optional completion callback. If the wrapped function's real last argument is a mapper, predicate, or handler, pass { args: N } so it keeps that function. call and apply also append callbacks while observing a returned thenable. A plugin that uses both paths may complete twice, and the package does not add a timeout when neither path completes.
Version 7 has no documented Node engine floor and our package check found no declaration file, so test the exact runtime and add local types if needed. Synchronous exceptions, method this binding, callback errors, Promise rejections, and variadic signatures all deserve paired tests in both calling styles. New application code should usually expose one async contract and avoid this ambiguity.
Patterns
Await an error-first callback function promisify-callback
import { promisify } from 'promise-breaker';
const readUser = promisify((id, done) => {
database.get(id, done);
});
const user = await readUser('42');The implementation callback must use `(error, result)`. Several success arguments are outside this wrapper's documented shape.
Call the same wrapper with a callback keep-callback-caller
readUser('42', (error, user) => {
if (error) return console.error(error);
console.log(user);
});Ignore the return value. In callback form, `promisify` creates an internal Promise that the README says never settles.
Add callbacks to async code callbackify-promise
import { callbackify } from 'promise-breaker';
const loadConfig = callbackify(async (file) => {
const text = await fs.promises.readFile(file, 'utf8');
return JSON.parse(text);
});
const config = await loadConfig('config.json');Without a callback, the wrapper returns the implementation's Promise; callback calls receive an error-first result.
Preserve a trailing mapper function protect-function-argument
const mapAsync = callbackify({ args: 2 }, async (items, mapper) => {
return Promise.all(items.map(mapper));
});
const doubled = await mapAsync([1, 2], (n) => n * 2);The `args: 2` option stops `callbackify` from treating `mapper` as the completion callback.
Invoke a callback-or-Promise plugin call-unknown-style
import { call } from 'promise-breaker';
async function runPlugin(plugin, input) {
return call(plugin.transform, plugin, input);
}
const output = await runPlugin(plugin, source);Pass the owner as `thisArg`. No timeout is created if the plugin returns no thenable and never calls its callback.
Invoke unknown-style code with an array apply-arguments
import { apply } from 'promise-breaker';
const args = ['report.csv', { encoding: 'utf8' }];
const result = await apply(loader, loaderContext, args);`apply` appends a callback to this array. Reject plugins that also return a Promise after calling that callback.
Place a callback explicitly with addPromise control-promise-wrapper
import { addPromise } from 'promise-breaker';
function lookup(id, options, done) {
return addPromise(done, (finish) => {
legacyLookup(id, options, finish);
});
}Use the lower-level helper when automatic trailing-callback placement is unsafe for the real signature.
Wrap async work with addCallback control-callback-wrapper
import { addCallback } from 'promise-breaker';
function calculate(input, done) {
return addCallback(done, async () => {
const value = await expensiveStep(input);
return value * 2;
});
}When `done` exists, `addCallback` reports fulfillment or rejection through it and returns undefined.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| pify | npm | Use it when conversion only runs from callback functions toward Promise-returning functions. |
| universalify | npm | Use it for focused fromCallback and fromPromise wrappers with a similar dual calling convention. |
| thenify | npm | Use it when promisifying Node callbacks, including callbacks that return several success values. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

