DEV Community

Cover image for I wrote a new Javascript library for promises
Mina Luke
Mina Luke

Posted on

I wrote a new Javascript library for promises

History of promises

For the past couple of years after using Promises and async/await a lot in Node and in browsers. I wanted to share my thoughts around what I like and what I do not like and what libraries I have used to gain more control over promises and finally why I created a new library to handle promises with some powerful customizations.

Let's go back a little bit in time.
Around 3-4 years ago. Bluebird was the best library at that time with lots of tools included in the library, map, race, any, sleep, and so much more.
Bluebird at that time was the defacto when you want to use Pormises in Javascript.

Time moved, and async/await come in javascript, it was a big big change. We started immediately to write all promises in async/await and enjoying the readability of it.

With Babel things become easier, you want to use async/await in old browsers, you can do it no problem just install some babel presets and that's all.

What is missing?

One of the things I really like about bluebird is the concurrent functionality it exposes. You can run promises in patches. Super feature isn't it? Personally, I used bluebird map function because it supports the concurrent param. What I do not like about bluebird is that it introduced a new classes/objects and I have to use them instead of using the native promises functions. Also there is no way to force stop the execution of these promises or be notified after every single running patch of promises.

After introducing Promise.allSettled I can not see how would we use bluebird to run promises in patches and use Promise.allSettled at the same time.

Other libraries like p-limit has made use of native promises and support conncurrency flag however it neither support Promise.allSettled no callback function between chunks.

I wrote a new library

I wrote my own implementation that supports these points:

  1. stick to native promises and do not introduce new methods in the Promise prototype.

  2. run promises in chunks with both Promise.all and Promise.allSettled flavors where every single chunk runs (n) number of promises in parallel.

  3. slow down the execution by introducing sleep/timeout function between chunks.

  4. call a custom function after every single chunk.

  5. force stop the promises execution for some reason in the middle.

  6. use Promise.allSettled in browsers that do not support it.

Let me introduce you to this new library: chunk-promise.

It supports all of the above and at the same time it uses native promise.

chunk-promise is a tiny library that can be used to run a list of native promises in chunks/patches by creating a promise chain with some optional customization that gives you full control over these promises.

It supports running both Promise.all and Promise.allSettled flavors in chunks. It can be used to run Promise.allSettled in browsers that do not support it. It can be combined with async/await.

API

Lets see a simple example on how to run promises in patches:

const { chunkPromise, PromiseFlavor } = require('chunk-promise');

const promiseArr = [
  () => Promise.resolve(1),
  () => Promise.reject(2),
  () => Promise.resolve(3),
  () => Promise.reject(4),
  () => Promise.resolve(5)
];

chunkPromise(promiseArr, {
  concurrent: 2,
  promiseFlavor: PromiseFlavor.PromiseAll // You may use PromiseAllSettled here
})
  .then(res => {})
  .catch(err => {});

You can also inject a callback function to be called after every single chunk as follows:

const { chunkPromise, PromiseFlavor } = require('chunk-promise');

const promiseArr = [
  () => Promise.reject(1),
  () => Promise.reject(2),
  () => Promise.resolve(3),
  () => Promise.reject(4),
  () => Promise.resolve(5)
];

chunkPromise(promiseArr, {
  concurrent: 2,
  promiseFlavor: PromiseFlavor.PromiseAllSettled,
  callback: async (chunkResults, index, allResults) => {
    if (chunkResults.some(p => p.status === 'fulfilled')) {
      console.log(`chunk (${index}): has success results`);
    } else {
      console.log(`chunk (${index}): has no success results`);
    }
  }
}).then(res => {});

As you can see at the above example callback is an async function that has access to these 3 params:

  • chunkResults: the current chunk value.
  • chunkIndex: the current chunk index.
  • allResults: the results of the promises so far.

With sleep function:

chunkPromise(promiseArr, {
  concurrent: 2,
  promiseFlavor: PromiseFlavor.PromiseAll,
  sleepMs: 2000
})

You can even force stop the execution inside the callback by throwing ChunkPromiseCallbackForceStopError error.

async function callback(chunkResults, index, allResults) => {
    console.log(`chunk (${index}): has success results`);
    if (index === 1) {
      throw new ChunkPromiseCallbackForceStopError(
        `Callback force stop at chunk index ${index}`
      );
    }
  }

Some more examples are here: examples

I would really appreciate any suggestions or any feedback on this library, please write me comment here :)

Top comments (0)