{"id":15975,"library":"call-me-maybe","title":"Flexible Async API (Callback or Promise)","description":"The `call-me-maybe` package provides a utility function designed to simplify the creation of JavaScript APIs that can gracefully accept either a traditional Node.js-style error-first callback or return a Promise for asynchronous operations. It abstracts away the common boilerplate logic needed to detect the presence of a callback argument, allowing API developers to write their core asynchronous code once. This approach helps in accommodating both classic callback-based consumers and modern Promise-driven codebases without needing to implement separate logic paths or external promisification utilities. The package is currently stable at version `1.0.2` and typically maintains a low release cadence, focusing on stability and bug fixes. Its key differentiator is enabling internal dual-interface support, streamlining the API design process for libraries targeting a broad audience.","status":"active","version":"1.0.2","language":"javascript","source_language":"en","source_url":"https://github.com/limulus/call-me-maybe","tags":["javascript","promise","callback","denodeify","promisify","carlyraejepsen"],"install":[{"cmd":"npm install call-me-maybe","lang":"bash","label":"npm"},{"cmd":"yarn add call-me-maybe","lang":"bash","label":"yarn"},{"cmd":"pnpm add call-me-maybe","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The package exports its main utility function as a default export, compatible with both ESM `import` and CJS `require` syntax.","wrong":"import { callMeMaybe } from 'call-me-maybe';","symbol":"callMeMaybe","correct":"import callMeMaybe from 'call-me-maybe';"},{"note":"When using CommonJS, the default export is directly returned by `require()`. Accessing it as a named property will fail.","wrong":"const { callMeMaybe } = require('call-me-maybe');","symbol":"callMeMaybe (CJS)","correct":"const callMeMaybe = require('call-me-maybe');"}],"quickstart":{"code":"import callMeMaybe from 'call-me-maybe';\n\ninterface FetchOptions {\n  delay?: number;\n  shouldError?: boolean;\n}\n\n/**\n * Simulates an asynchronous data fetch that can be consumed via callback or Promise.\n */\nfunction fetchData(id: string, options?: FetchOptions, callback?: (err: Error | null, data?: string) => void): Promise<string> | void {\n  // callMeMaybe expects the callback as the last argument. \n  // The second argument is an async function that receives resolve/reject for the Promise path.\n  return callMeMaybe(callback, async (resolve, reject) => {\n    try {\n      const delay = options?.delay ?? 100;\n      await new Promise(res => setTimeout(res, delay)); // Simulate network delay\n\n      if (options?.shouldError || id === 'error') {\n        throw new Error(`Failed to fetch data for ID: ${id}`);\n      }\n      const data = `Successfully fetched data for ID: ${id} (delayed by ${delay}ms)`;\n      resolve(data);\n    } catch (error: any) {\n      reject(error);\n    }\n  });\n}\n\n// --- Usage Examples ---\n\n// 1. Promise-based consumption\nconsole.log('--- Promise Usage ---');\nfetchData('user-promise', { delay: 50 })\n  .then(data => console.log('Promise resolved:', data))\n  .catch(err => console.error('Promise rejected:', err.message));\n\nfetchData('error-promise', { shouldError: true, delay: 20 })\n  .then(data => console.log('Promise resolved (unexpected):', data))\n  .catch(err => console.error('Promise rejected:', err.message));\n\n// 2. Callback-based consumption\nconsole.log('\\n--- Callback Usage ---');\nfetchData('user-callback', { delay: 70 }, (err, data) => {\n  if (err) {\n    console.error('Callback error:', err.message);\n    return;\n  }\n  console.log('Callback success:', data);\n});\n\nfetchData('error-callback', { shouldError: true, delay: 30 }, (err, data) => {\n  if (err) {\n    console.error('Callback error:', err.message);\n    return;\n  }\n  console.log('Callback success (unexpected):', data);\n});\n\n// 3. Illustrating the return type (for environments where no callback is provided)\nconsole.log('\\n--- Mixed Usage (implicitly Promise) ---');\nconst resultWithoutCallback = fetchData('implicit-promise', { delay: 10 });\nif (resultWithoutCallback instanceof Promise) {\n  resultWithoutCallback\n    .then(data => console.log('Implicit Promise resolved:', data))\n    .catch(err => console.error('Implicit Promise rejected:', err.message));\n} else {\n  // This branch is only hit if a callback was provided and handled internally.\n  console.log('Function returned void (callback handled it).');\n}","lang":"typescript","description":"This quickstart demonstrates how to create a function that transparently supports both error-first callbacks and Promises using `call-me-maybe`, showing both consumption patterns."},"warnings":[{"fix":"Upgrade to `call-me-maybe@1.0.2` or newer to resolve issues with `global` context in CommonJS environments.","message":"Older versions of `call-me-maybe` (prior to `v1.0.2`) could encounter `ReferenceError: global is not defined` in certain CommonJS environments. This was due to an implicit reliance on the `global` object that might not be available or correctly polyfilled in all CJS setups.","severity":"gotcha","affected_versions":"<1.0.2"},{"fix":"Rigorously handle all success and error paths within your async function to guarantee `resolve`/`reject` or callback invocation for every execution branch.","message":"When defining your asynchronous logic within the `callMeMaybe` wrapper, it is crucial to ensure that you always call either `resolve` or `reject` (or the provided callback with `err` or `data`). Failure to do so will result in hanging operations, where Promises never settle and callbacks are never invoked, leading to unresponsive applications.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Carefully design your function signature and argument parsing. If your API has complex optional arguments, you might need a wrapper to ensure `callMeMaybe` always receives the true callback (or `undefined`) as its dedicated `callback` argument.","message":"The `callMeMaybe` utility assumes the *last* argument passed to your API function is the potential callback. If your function's signature changes, or if you have optional arguments that could be mistaken for a callback, ensure they are handled correctly before passing to `callMeMaybe` to avoid unexpected behavior.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"For ESM, use `import callMeMaybe from 'call-me-maybe';`. For CommonJS, use `const callMeMaybe = require('call-me-maybe');`.","cause":"This error typically occurs when attempting to import `callMeMaybe` as a named export (`import { callMeMaybe } from 'call-me-maybe';`) or incorrectly destructuring in CommonJS (`const { callMeMaybe } = require('call-me-maybe');`). The package uses a default export.","error":"TypeError: callMeMaybe is not a function"},{"fix":"Upgrade your `call-me-maybe` package to version `1.0.2` or newer. If this is not possible, ensure your CJS environment explicitly defines `global` if it's expected.","cause":"This error was a known bug in `call-me-maybe` versions prior to `1.0.2` when used in specific CommonJS environments that did not properly define or polyfill the `global` object.","error":"ReferenceError: global is not defined"}],"ecosystem":"npm"}