Unified Async/Sync Function Execution
run-async is a utility method designed to normalize the execution of functions that can operate synchronously, asynchronously via a `this.async()` callback, or by returning a Promise. This library is particularly useful for authors of middleware or plugins that need to accept user-provided functions with varying asynchronous patterns, ensuring a consistent execution flow. The current stable version is 4.0.6, and the project demonstrates an active maintenance cadence, addressing dependency issues and enhancing functionality across major versions. A key differentiator is its ability to abstract away the underlying async mechanism, providing a single interface, and its current status as a dependency-free package since version 2.4.1, which improves reliability and reduces supply chain risks.
Common errors
-
TypeError: this.async is not a function
cause The function passed to `runAsync` is not being called with the correct `this` context, which typically provides the `async` method.fixEnsure `runAsync` is called such that the wrapped function's `this` context is correctly bound or provided by `run-async` itself. Avoid using arrow functions for the wrapped function if you intend to use `this.async()`, as arrow functions lexically bind `this`. -
Error: Callback was already called.
cause This error typically occurs when the `done()` callback (from `this.async()`) or a Promise's `resolve`/`reject` function is invoked more than once within the wrapped function.fixReview your asynchronous logic to ensure that `done()`, `resolve()`, or `reject()` are called exactly once per `runAsync` invocation, even in error scenarios. Add guards to prevent multiple calls.
Warnings
- breaking Version 2.0.0 changed the `runAsync` signature. If your Node.js version supports native Promises, `runAsync` will now return a Promise, altering how it's consumed compared to earlier versions that exclusively used callbacks.
- breaking The `runAsync.cb` function, introduced in v2.1.0, requires the wrapped function to have a fixed number of parameters. Variable argument functions or functions with `arguments` objects may not behave as expected.
- gotcha Prior to v2.2.0, Promise polyfill behavior was conditional. Since v2.2.0, `runAsync` consistently returns a Promise, relying on the 'Pinkie' polyfill if native Promises are not available in the Node.js environment.
- gotcha The `is-promise` dependency was removed in v2.4.1 due to issues with the upstream package, making `run-async` dependency-free. Older versions might have experienced unexpected behavior or issues related to this dependency.
- gotcha The Node.js 0.10 Promise polyfill was removed in v2.3.0. Users on very old Node.js 0.10 environments might experience Promise-related issues without the polyfill.
Install
-
npm install run-async -
yarn add run-async -
pnpm add run-async
Imports
- runAsync
const runAsync = require('run-async');import { runAsync } from 'run-async'; - runAsync.cb
import { cb } from 'run-async';import { runAsync } from 'run-async'; runAsync.cb(...); - RunAsync function types
import type { RunAsync } from 'run-async';
Quickstart
import { runAsync } from 'run-async';
// A helper to demonstrate how runAsync normalizes different function types
const printAfter = async (func: Function) => {
const cb = (err: Error | null, returnValue: unknown) => {
if (err) {
console.error('Error:', err);
return;
}
console.log(returnValue);
};
// Execute the function using runAsync, passing a callback
// runAsync returns a function that takes arguments for func
try {
const result = await runAsync(func, cb)();
// If func returns a Promise, runAsync resolves it. The callback also fires.
// We'll catch the potential Promise resolution here.
// The callback provided to runAsync will still be called.
} catch (error) {
console.error('Caught an error from runAsync execution:', error);
}
};
console.log('--- Using this.async ---');
printAfter(function (this: any) {
const done = this.async();
setTimeout(() => {
done(null, 'done running with callback');
}, 10);
});
console.log('\n--- Returning a Promise ---');
printAfter(function () {
return new Promise(resolve => {
setTimeout(() => resolve('done running with promises'), 5);
});
});
console.log('\n--- Synchronous function ---');
printAfter(function () {
return 'done running sync function';
});
// Example using runAsync.cb for Node.js callback style
console.log('\n--- Using runAsync.cb (Node.js callback style) ---');
runAsync.cb(
(a: number, b: number, cb: (err: Error | null, result: number) => void) => {
setTimeout(() => cb(null, a + b), 20);
},
(err: Error | null, result: number) => {
if (err) console.error('Error with runAsync.cb:', err);
else console.log(`runAsync.cb result: ${result}`);
}
)(5, 7);