p-props

Run promises concurrently and collect the results by key, for both Object and Map inputs.

Library
npm
v6.1.0
201stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
39/100Needs Attention
Development Activity0
Maintenance20
Community56
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality88
Innovation82
Learning Curve25

p-props is a tiny utility from the sindresorhus promise-fun family that behaves like Promise.all(), except it resolves the values of a plain Object or a Map instead of an array, returning a container of the same shape with each entry’s value awaited in place. An optional mapper function can transform each resolved value before it’s placed back into the result, and concurrency is controlled by delegating to p-map under the hood.

Alongside the default fail-fast export, it also ships pPropsAllSettled, a variant that mirrors Promise.allSettled() semantics — every entry resolves to a { status, value } or { status, reason } object instead of the whole call rejecting on the first failure. Both functions preserve whether the input was an Object or a Map, so callers never have to manually convert between Object.entries() and array form.

What You Get

  • A default export (pProps) that resolves every promise value in an Object or Map concurrently and returns the same container shape with awaited values
  • A named export (pPropsAllSettled) that never rejects — each entry comes back as a {status: 'fulfilled', value} or {status: 'rejected', reason} record
  • An optional mapper(value, key) callback on both functions to transform each resolved value before it’s written back
  • Pass-through concurrency control via the same options object accepted by p-map (e.g. {concurrency: 4})
  • Full TypeScript type inference — the return type mirrors the input’s key/value shape, with or without a mapper

Common Use Cases

  • Fetching several named API endpoints in parallel and assembling the responses into one result object
  • Running independent async lookups (config, feature flags, user profile) concurrently instead of sequentially awaiting each one
  • Building a summary object where some values are promises and others are already-resolved constants, without hand-rolling the merge
  • Using pPropsAllSettled to gather partial results from several concurrent calls where some may fail without aborting the rest

Under The Hood

Architecture p-props ships as a single ~30-line index.js exporting two functions, pProps and pPropsAllSettled. Both share the same normalization strategy: a toEntries helper converts the input (Object or Map) into an array of [key, value] pairs, the array is handed to p-map for concurrent iteration with an optional per-entry mapper callback, and a fromEntries helper reconstructs a container of the same type (Object or Map) as the original input from the resolved entries. There is no internal state, no classes, and no branching beyond the Object/Map type check — all iteration, concurrency limiting, and error propagation is delegated entirely to p-map, making this package a thin, symmetric adapter layer on top of it.

Tech Stack The package is pure ESM ("type": "module") with a single runtime dependency, p-map ^6.0.0, and targets Node.js >=18. Type declarations are hand-authored in index.d.ts rather than compiled from TypeScript source, so there is no build step — the published package ships index.js and index.d.ts directly. Development tooling consists of xo (an opinionated ESLint preset) for linting, ava for test execution, tsd for compile-time type-contract testing, and delay as a test-only helper for simulating async latency. CI runs the full npm test (xo + ava + tsd) across a Node 18/20 matrix via GitHub Actions.

Code Quality test.js contains sixteen ava test cases covering both functions against Object and Map inputs, mixed promise/non-promise values, mapper callbacks (including per-key assertions on the value the mapper receives), rejection propagation for the fail-fast variant, per-entry fulfilled/rejected states for the allSettled variant (including a mapper that itself throws), and empty-input edge cases — a comprehensive suite relative to the implementation’s small size. There is no explicit error handling or wrapping in the implementation itself; the fail-fast path relies entirely on native promise rejection propagating up through p-map, and pPropsAllSettled wraps each entry in its own try/catch to convert rejections into settled records. Naming (toEntries, fromEntries) is terse and consistent with the rest of the sindresorhus utility catalog, and type correctness is enforced separately via tsd against index.test-d.ts.

API Design The API surface is a single function call per variant with a signature deliberately mirroring Promise.all()/Promise.allSettled(), so existing familiarity transfers directly. The standout ergonomic choice is shape symmetry: whatever container type (Object or Map) is passed in comes back out, so callers never manually convert with Object.entries()/Object.fromEntries() or new Map(). Return types are fully inferred per key when no mapper is supplied, and narrow further when one is. Choosing between fail-fast and settle-all semantics is a matter of picking which export to call, not restructuring the call site — a small but effective reduction in boilerplate for a very common concurrent-fan-out pattern.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search