async
Higher-order async utility functions for callback, async/await, and promise-based control flow in Node.js and the browser.
Repository Health
Technical Analysis
Async is a widely-used JavaScript utility library providing around 70 functions for managing asynchronous control flow: collection iteration (each, map, filter, reduce, sortBy), control-flow composition (waterfall, series, parallel, auto), and concurrency utilities (queue, cargo, retry). Every function follows Node’s error-first callback convention while also transparently accepting native async functions and Promise-returning functions, and returns a Promise when no callback is supplied.
Originally created in 2010, before Promises or async/await existed in JavaScript, Async remains one of the most depended-upon packages in the npm ecosystem, still receiving maintenance and pulling over 100 million weekly downloads. Version 3 rewrote the internals as ES modules with full async-function support while preserving the same callback-first API that made it a de facto standard for pre-Promise Node.js code.
What You Get
- Around 70 async utility functions covering collections (each, map, filter, reduce, sortBy), control flow (series, parallel, waterfall, auto, race), and utilities (queue, cargo, retry, memoize)
- Uniform support for three calling conventions — Node-style error-first callbacks, native async/await functions, and Promises — mixed freely within the same call
- A dependency-graph task runner (auto/autoInject) that resolves execution order and concurrency automatically from named task requirements
- Bounded-concurrency primitives (queue, cargo, and *Limit variants) for throttling parallel work against rate limits or resource constraints
- Both a CommonJS build and a native ESM build (plus the separate async-es package), so the same API works in Node, bundlers, and directly in the browser
Common Use Cases
- Rate-limiting concurrent API calls with mapLimit/queue so a batch job doesn’t overwhelm a downstream service
- Orchestrating a multi-step startup sequence with auto, where some steps depend on the results of others finishing first
- Migrating or maintaining legacy Node.js callback-based codebases without rewriting call sites to promises
- Iterating over large collections asynchronously with eachSeries/mapSeries when operations must run in strict order, such as sequential file writes
Under The Hood
Architecture Async’s roughly 5,500 lines under lib/ are organized one function per file (lib/auto.js, lib/queue.js, lib/waterfall.js, and so on), each a thin wrapper around shared primitives in lib/internal/: DoublyLinkedList.js backs the queue’s task list, wrapAsync.js/asyncify.js normalize callback-, async-function-, and Promise-returning iteratees into one internal calling convention, and eachOfLimit.js/parallel.js implement the concurrency-limited iteration loop that most collection functions delegate to. lib/index.js re-exports every public function. The dependency-graph runner in auto.js builds an adjacency structure over the tasks object and drives each task’s callback through once()/onlyOnce() guards, starting a task only once all its listed dependencies have resolved. Because nearly every public function is a shim over the same wrapAsync/eachOfLimit internals, a change to the calling-convention normalization or the queue’s internal linked-list would ripple through virtually the entire public API.
Tech Stack The package ships with zero runtime dependencies — a deliberate choice for a foundational utility library — and relies on Babel (preset-es2015/es2017) to transpile the ES-module source under lib/ into the CommonJS dist/async.js published to npm, alongside a separate async-es package for pure-ESM consumers; Rollup produces the browser bundle. Testing runs on Mocha with Chai assertions and nyc coverage in Node, plus a parallel browser pass via Karma (karma-browserify, karma-firefox-launcher, karma-safari-launcher) exercising the same suite in real browsers. ESLint with @babel/eslint-parser enforces style, and GitHub Actions CI runs lint followed by the full test matrix on every push, with Coveralls tracking coverage. Documentation is generated via JSDoc from inline @name/@category annotations in each lib file.
Code Quality The test/ directory mirrors lib/ almost one-to-one, with dozens of individual test cases per function covering callback-style, async-function, and Promise-based invocation paths plus explicit error-propagation and double-callback edge cases. Error handling is explicit throughout: internal helpers like once.js and onlyOnce.js exist specifically to guard against a callback being invoked twice, and wrapAsync.js/asyncify.js branch explicitly on whether a passed function is a plain callback function, an AsyncFunction, or an async generator rather than silently coercing mismatches. There is no static type system — plain ES modules, no TypeScript — but ESLint enforces consistent style, and CI runs the full Node and browser test suites on every push.
API Design Async’s defining design choice is accepting three different calling conventions — error-first callbacks, native async functions, and Promise-returning functions — through every one of its roughly 70 functions via the wrapAsync/asyncify normalization layer, and returning a Promise itself when no callback is supplied. Function naming is highly consistent: a base verb (each, map, filter, sortBy) composes with Series (strict sequential) and Limit (bounded concurrency) suffixes, so learning one function’s variants teaches the pattern for the rest of the library. This uniformity is also the biggest cost for newcomers today: because the library predates Promises and async/await in the language, much of its surface area (waterfall, auto, series) now solves a problem the language itself later absorbed. Documentation stays thorough regardless — a JSDoc-generated per-function reference plus inline @example blocks in every source file keep the boilerplate for adopting any single function low.
Used by 23 apps in this directory
Airbyte
Developer Tools · Data Engineering
Open-source ELT platform with 600+ connectors for moving data from any source to warehouses, lakes, and AI agents.
Artillery
Devops · Developer Tools
Cloud-scale load testing and functional testing for APIs, WebSockets, gRPC, and headless browsers, distributed across AWS Lambda or Fargate with zero infrastructure to manage.
Bigcapital
Invoicing Finance
Self-hostable double-entry accounting platform with invoicing, inventory, multi-currency, and real-time financial reporting for small and medium businesses.
Cal.diy
Scheduling
The 100% MIT-licensed, community-driven scheduling platform — self-host your own booking infrastructure with no enterprise strings attached.
Cocos Engine
Developer Tools · Game Development · Design Tools
Open-source, cross-platform 2D/3D game engine with Vulkan, Metal, and WebGL support for web, mobile, and instant gaming platforms
Codebuff
AI Code Assistants
An open-source AI coding assistant that coordinates specialized agents to edit your codebase from natural language — including Freebuff, a free, ad-supported version powered entirely by open-source models like DeepSeek and Kimi.
CodeSandbox
Code Editors · Developer Tools
Instantly ready browser-based IDE that runs full npm dependency resolution and transpilation entirely client-side, with no server needed.
Countly
Analytics · Marketing
Privacy-first, self-hosted analytics and customer engagement platform with full data ownership, GDPR compliance, and AI-powered insights across mobile, web, desktop, and IoT.
Directus
CMS · Low Code Platforms
Connect any SQL database and get instant REST and GraphQL APIs, a visual management Studio, and a native MCP server for AI agents — free for most organizations.