feat(core-utils): add mapAsync and reduceAsync utils

This commit is contained in:
nathan-pichon 2022-12-08 17:17:11 +01:00
parent ea3f7fa37e
commit 0168a2758a
No known key found for this signature in database
6 changed files with 142 additions and 16 deletions

View File

@ -0,0 +1,70 @@
'use strict';
const { mapAsync, reduceAsync } = require('../async');
describe('Async utils', () => {
describe('mapAsync', () => {
test('Should return a simple array of numbers', async () => {
const numberPromiseArray = [Promise.resolve(1), Promise.resolve(2)];
const mapFunc = mapAsync(numberPromiseArray);
const result = await mapFunc((number) => number + 1);
expect(result).toEqual([2, 3]);
});
test('Should work with mix of promises and values', async () => {
const numberMixArray = [1, Promise.resolve(2)];
const mapFunc = mapAsync(numberMixArray);
const result = await mapFunc((number) => number + 1);
expect(result).toEqual([2, 3]);
});
test('Should throw an error', async () => {
const numberPromiseArray = [Promise.resolve(1), Promise.resolve(2)];
const mapFunc = mapAsync(numberPromiseArray);
await expect(async () => {
await mapFunc(() => {
throw new Error('test');
});
}).rejects.toThrow('test');
});
});
describe('reduceAsync', () => {
test('Should return a incremented number', async () => {
const numberPromiseArray = [Promise.resolve(1), Promise.resolve(2)];
const reduceFunc = reduceAsync(numberPromiseArray);
const result = await reduceFunc(
(previousValue, currentValue) => previousValue + currentValue,
10
);
expect(result).toEqual(13);
});
test('Should work with mix of promises and values', async () => {
const numberMixArray = [1, Promise.resolve(2)];
const reduceFunc = reduceAsync(numberMixArray);
const result = await reduceFunc(
(previousValue, currentValue) => previousValue + currentValue,
10
);
expect(result).toEqual(13);
});
test('Should throw an error', async () => {
const numberPromiseArray = [Promise.resolve(1), Promise.resolve(2)];
const reduceFunc = reduceAsync(numberPromiseArray);
await expect(async () => {
await reduceFunc(() => {
throw new Error('test');
});
}).rejects.toThrow('test');
});
});
});

View File

@ -0,0 +1,69 @@
'use strict';
function pipeAsync(...methods) {
return async (data) => {
let res = data;
for (const method of methods) {
res = await method(res);
}
return res;
};
}
/**
* Map function callback.
* @callback mapAsyncCallback
* @param {*} value
* @param {number} index
*/
/**
* Async iteration over an array of promises
* @param {promise<*>[]} promiseArray
* @returns {function(callback: mapAsyncCallback): promise<*[]>}
*/
function mapAsync(promiseArray) {
/**
* @param {mapAsyncCallback} callback
* @returns promise<*[]>
*/
return (callback) => {
const transformedPromiseArray = promiseArray.map(async (promiseValue, index) => {
const value = await promiseValue;
return callback(value, index);
});
return Promise.all(transformedPromiseArray);
};
}
/**
* Reduce function callback.
* @callback reduceAsyncCallback
* @param {*} previousValue
* @param {*} currentValue
* @param {number} index
*/
/**
* Async chain over an array of promises
* @param {promise<*>[]} promiseArray
* @returns {function(callback: reduceAsyncCallback, initialValue?: *): promise<*>}
*/
function reduceAsync(promiseArray) {
/**
* @param {reduceAsyncCallback} callback
* @param {*} [initialValue]
* @returns promise<*>
*/
return (callback, initialValue) =>
promiseArray.reduce(async (previousPromise, currentValue, index) => {
const previousValue = await previousPromise;
return callback(previousValue, await currentValue, index);
}, Promise.resolve(initialValue));
}
module.exports = {
mapAsync,
reduceAsync,
pipeAsync,
};

View File

@ -37,7 +37,7 @@ const providerFactory = require('./provider-factory');
const pagination = require('./pagination'); const pagination = require('./pagination');
const sanitize = require('./sanitize'); const sanitize = require('./sanitize');
const traverseEntity = require('./traverse-entity'); const traverseEntity = require('./traverse-entity');
const pipeAsync = require('./pipe-async'); const { pipeAsync } = require('./async');
const convertQueryParams = require('./convert-query-params'); const convertQueryParams = require('./convert-query-params');
const importDefault = require('./import-default'); const importDefault = require('./import-default');
const template = require('./template'); const template = require('./template');

View File

@ -1,13 +0,0 @@
'use strict';
module.exports =
(...methods) =>
async (data) => {
let res = data;
for (const method of methods) {
res = await method(res);
}
return res;
};

View File

@ -4,7 +4,7 @@ const { isArray } = require('lodash/fp');
const traverseEntity = require('../traverse-entity'); const traverseEntity = require('../traverse-entity');
const { getNonWritableAttributes } = require('../content-types'); const { getNonWritableAttributes } = require('../content-types');
const pipeAsync = require('../pipe-async'); const { pipeAsync } = require('../async');
const visitors = require('./visitors'); const visitors = require('./visitors');
const sanitizers = require('./sanitizers'); const sanitizers = require('./sanitizers');

View File

@ -2,7 +2,7 @@
const { curry } = require('lodash/fp'); const { curry } = require('lodash/fp');
const pipeAsync = require('../pipe-async'); const { pipeAsync } = require('../async');
const traverseEntity = require('../traverse-entity'); const traverseEntity = require('../traverse-entity');
const { removePassword, removePrivate } = require('./visitors'); const { removePassword, removePrivate } = require('./visitors');