Skip to content

Instantly share code, notes, and snippets.

@Mazuh
Last active November 29, 2019 13:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mazuh/c977350b67e52870a6f764be73659f80 to your computer and use it in GitHub Desktop.
Save Mazuh/c977350b67e52870a6f764be73659f80 to your computer and use it in GitHub Desktop.
Queued Promises maker
import chunk from 'lodash/chunk';
/**
*
* Util to map values to `Promise`s and invoke them in queue order,
* being assured each `Promise` is resolved before the next be created.
*
* It reduces `data` array to a `Promise` which is the accumulated chain
* of async tasks resulting of each element mapped thru `asyncIteratee`.
* As a chain, each `asyncIteratee` follows a queue order of call, and
* may optionally have the data chunked or delayed.
*
* @param {Array} data - array of data to be given to `asyncIteratee` calls
* @param {Function} asyncIteratee - function mapping `data` chunks to `Promise`s
* @param {Object} [options={}]
* @param {number} [options.chunksSize=1] - size of chunks to split `data`
* before passing thru iteratee
* @param {number} [options.interval=0] - delay (in ms) before every
* iteratee resolves
* @return {Promise} resulting of all chaining
*/
export function applyToQueuedPromises(data, asyncIteratee, options = {}) {
const { chunksSize = 1, interval = 0 } = options;
return chunk(data, chunksSize).reduce((chainedPromises, ...mapArgs) => {
const makeNextPromise = () => new Promise((resolve, reject) => {
asyncIteratee(...mapArgs).then(() => setInterval(resolve, interval)).catch(reject);
});
return chainedPromises.then(makeNextPromise);
}, Promise.resolve());
}
/**
* Uses `applyToQueuedPromises`, but with a middleware to provide
* the percentage of tasks runned thru `asyncIteratee`.
*
* @param {*} data - to be forwarded
* @param {*} asyncIteratee - to be forwarded (with middleware applied)
* @param {...any} argsRest - rest of arguments to be forwarded.
* @return {Promise} same as `applyToQueuedPromises`
*/
export function applyToQueuedPromisesWithPercentage(data, asyncIteratee, ...argsRest) {
const iterateeAdapter = (chunkedPart, index, allChunks) => {
const progressStep = index + 1;
const totalSteps = allChunks.length;
const progressPercentage = (progressStep * 100) / totalSteps;
return asyncIteratee(chunkedPart, progressPercentage);
};
return applyToQueuedPromises(data, iterateeAdapter, ...argsRest);
}
@Mazuh
Copy link
Author

Mazuh commented Nov 29, 2019

Example of usage on a state machine like React implicitly has:

  bulkFetch = async (allValues) => {
    try {
      this.setFetchingProgress(0);
      this.assignBulkPendingStatus(allValues);
      await applyToQueuedPromisesWithPercentage(allValues, async (fewValues, percentage) => {
        try {
          const { data } = await MyAPI.getAll({ criteria: fewValues });
          this.setFetchingProgress(percentage);
          this.assignBulkFetchResult(fewValues, 'OK');
        } catch (error) {
          this.assignBulkFetchResult(fewValues, 'ERROR');
        }
      }, {
        chunksSize: 150, // fewValues won't have more than 150 length
        interval: 1000, // 1s cooldown before next fetch
      });
    } finally {
      this.setFetchingProgress(100);
    }
  }

More robust lib for this use case: https://github.com/energizer91/smart-request-balancer
The snippet in this gist, however, is meant to be agnostic to any async task, have easier support to chunking and show progress percentage... but it has much less features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment