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
GDevelop
Design Tools · Developer Tools · Game Development
No-code, open-source game engine for building 2D, 3D and multiplayer games — publish to iOS, Android, Steam and the web.
GrowthBook
Ab Testing Experimentation · Analytics · Developer Tools
Open source feature flags, A/B testing, and warehouse-native experimentation that queries your existing data infrastructure—no data movement required.
Kibana
Analytics · Monitoring
Your open source window into the Elastic Stack — query, visualize, and act on data stored in Elasticsearch with real-time dashboards, AI-assisted search, and automated alerting.
NodeBB
Community
Modern Node.js forum software with real-time WebSockets, multi-database support, and a plugin ecosystem — the community platform built for the open web and the Fediverse.
Novu
Developer Tools
Open-source communication infrastructure that connects your products and AI agents to every channel your users live on — Inbox, Email, SMS, Push, Chat, and more.
Open WebUI
AI Agents · AI Assistants
The extensible, privacy-first AI platform that runs Ollama, OpenAI, and any LLM backend behind a polished, feature-packed web interface.
overleaf
Collaboration · Productivity
Open-source, real-time collaborative LaTeX editor with sandboxed compilation and full TeXLive support for self-hosted academic and research teams.
PeerTube
Social Media
A federated, ActivityPub-based video hosting platform built by Framasoft — self-hostable instances interconnect into a network with no vendor lock-in, P2P-assisted streaming, and no ads.
Arize Phoenix
Analytics · Devops · Monitoring
Open-source AI observability platform for tracing, evaluating, and debugging LLM applications with built-in intelligence and MCP support.