Bluebird is a fully featured JavaScript promises library. Whether you use it in Node.js or in the browser, with plain JavaScript or TypeScript, Bluebird can make working with promises much easier.

Reactive programming with Observables is powerful, but it can be overwhelming at the beginning. If you are learning JavaScript or TypeScript, it is still useful to understand promises first. Promises are part of ES6, and async/await in ES2017 made asynchronous programming more convenient.

The standard promise toolset in Node.js and browsers is limited. That is why libraries like Bluebird can still be handy in older or specialized codebases.

The useful part: concurrency

One method that stands out is Promise.map(). It is similar to Array.prototype.map(), but the third parameter gives you control over the map operation. The important setting is concurrency.

The concurrency limit applies to promises returned by the mapper function. If concurrency is set to 3, the mapper will not keep creating more pending promises once three returned promises are still unresolved.

In practice, if you pass an array of 1000 objects to Promise.map() and specify a concurrency of 50, the mapping function can run in parallel, but never more than 50 times at once.

Use cases

  • Spread execution of a task over a longer period of time.
  • Keep CPU usage lower when background work should not interfere with foreground tasks.
  • Throttle calls to an external API to avoid overload, timeouts, or rate limits.
  • Smooth out memory spikes when mapping over large arrays.

Without concurrency, Promise.map() can start mapper work for many items as soon as it can. In the worst case, thousands of tasks run in parallel and cause avoidable out-of-memory errors.

Example

Here is a simplified example from a sports domain:

getAllPlayers() {
  return Teams.findAll().map(team => {
    return externalApi.getPlayersForTeam(team.id);
  }, {
    concurrency: 5
  })
  .then(players => _.flatten(players));
}

The code first loads all teams from the database, then maps each team to player data from an external API. The result is flattened with lodash. The concurrency setting prevents the external API from being hit with too many requests at the same time.

Things to keep in mind

  • There is no guarantee on the order in which Promise.map() calls the mapper function for array elements.
  • If you need sequential execution, look into Promise.mapSeries() or Promise.each().
  • For new code, also consider whether native promises, async iterators, or a small queue utility cover the use case with fewer dependencies.