@constantiner/fun-ctional
Version:
The library brings most of the familiar functional techniques (like functional composition) to asynchronous world with shining Promises
1,099 lines (974 loc) • 37.1 kB
JavaScript
/**
* @constantiner/fun-ctional
* The library brings most of the familiar functional techniques (like functional composition) to asynchronous world with shining Promises
*
* @author Konstantin Kovalev <constantiner@gmail.com>
* @version v0.6.6
* @link https://github.com/Constantiner/fun-ctional#readme
* @date 30 May 2019
*
* MIT License
*
* Copyright (c) 2018-2019 Konstantin Kovalev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined"
? factory(exports)
: typeof define === "function" && define.amd
? define(["exports"], factory)
: ((global = global || self), factory((global.funCtional = {})));
})(this, function(exports) {
"use strict";
var customPromiseHandlingSupportSupport = Symbol.for("Custom Promise Handling Support");
var addCustomPromiseHandlingSupport = function addCustomPromiseHandlingSupport(fn, customVersion) {
return (fn[customPromiseHandlingSupportSupport] = customVersion), fn;
};
var supportsCustomPromiseHandling = function supportsCustomPromiseHandling(fn) {
return fn[customPromiseHandlingSupportSupport];
};
/**
* Composable version of catch method for promises.
*
* It gets a value (a promise or not), resolves it and if resulting promise was rejected calls catch function.
*
* It allows to handle errors within acompose or apipe asynchronous composition chains to restore broken state etc.
*
* A sample with acompose:
*
* <pre><code>const resultOrFallback = await acompose(acatch(handleAndRecoverFn), canFailFn)(someInput);</code></pre>
*
* Standalone usage:
*
* <pre><code>const resultOrFallback = await acatch(handleAndRecoverFn)(requestDataAndReturnPromise());</code></pre>
*
* It is the same as the following:
*
* <pre><code>requestDataAndReturnPromise().catch(handleAndRecoverFn).then(resultOrFallback => console.log(resultOrFallback));</code></pre>
*
* @param {function} catchFn Is function to handle Promise's rejection.
* @returns {any => Promise} A function which expects any value as input (Promise or not) and returns a Promise.
*/
var acatch = function(catchFn) {
var handler = function handler(value) {
return Promise.resolve(value).catch(catchFn);
};
return addCustomPromiseHandlingSupport(handler, function(promise) {
return promise.catch(catchFn);
});
};
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]")
return Array.from(iter);
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
/**
* Checks if parameter is an iterable.
*
* @param {any} obj is target to check.
* @returns {boolean} Result of checking.
*/
var isIterable = function isIterable(obj) {
return Array.isArray(obj) || ((obj || obj === "") && typeof obj[Symbol.iterator] === "function");
};
/**
* Returns an array of values from arguments of some function.
* Arguments can be passed as arguments list ar as single iterable parameter.
*
* @param {array} args are input array (just arguments of some other function).
* If if consists of one element and this element is an iterable returns array from it.
* @returns {Promise} Resulting array of arguments.
*/
var extractResolvedArguments = function extractResolvedArguments(args) {
return args ? (args.length === 1 && isIterable(args[0]) ? _toConsumableArray(args[0]) : args) : [];
};
/**
* Asynchronous compose function (acompose stays for async-compose).
*
* The main purpose is to replace a Promise handling code like this:
* <pre><code>somePromise.then(normalize).then(upperCase).then(insertGreetings);</code></pre>
*
* with point-free style of functional compose syntax like the following:
* <pre><code>acompose(insertGreetings, upperCase, normalize)(somePromise);</code></pre>
*
* It is lazy and allows of reusing of promise handling chains.
*
* You can run acompose with Promise instance (for true asynchronous execution)
* or with any other object to use as usual functional composition.
* It produces a Promise and can be used in async/await context:
*
* <pre><code>const message = await acompose(insertGreetings, upperCase, normalize)(somePromise);</code></pre>
*
* It also allows to handle errors like for traditional Promise but only in the tail position of the chain:
*
* <pre><code>acompose(insertGreetings, upperCase, normalize)(somePromise).catch(e => console.error(e));</code></pre>
*
* @param {...function|Iterable.<*>} fns Are functions to compose chains of promises.
* @returns {(promise : Promise|any) => Promise} A function which expects any value as input (resolving to Promise) and returns a Promise.
*/
var acompose = function() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
return (
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(promise) {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
return _context.abrupt(
"return",
extractResolvedArguments(fns).reduceRight(function(promise, fn) {
var promiseHandler = supportsCustomPromiseHandling(fn);
if (promiseHandler) {
return promiseHandler(promise);
}
return promise.then(fn);
}, Promise.resolve(promise))
);
case 1:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function(_x) {
return _ref.apply(this, arguments);
};
})()
);
};
/* eslint-disable unicorn/prefer-spread */
/**
* Returns a promise which resolved to array of values from a passed iterable.
* An iterable can be a promise to resolve to iterable and then to array.
*
* @param {Promise|Iterable.<*>} arrayLike Is iterable or promise to resolve to.
* @returns {Promise} A promise to resolve to resulting array.
*/
var extractArrayFromArgument =
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(arrayLike) {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_context.t0 = Array;
_context.next = 3;
return Promise.resolve(arrayLike);
case 3:
_context.t1 = _context.sent;
return _context.abrupt("return", _context.t0.from.call(_context.t0, _context.t1));
case 5:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function extractArrayFromArgument(_x) {
return _ref.apply(this, arguments);
};
})();
var filterMergeMap = function filterMergeMap(filterFn) {
return (
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(element, index, array) {
var filterResult;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_context.next = 2;
return filterFn(element, index, array);
case 2:
filterResult = !!_context.sent;
return _context.abrupt("return", {
filterResult: filterResult,
element: element
});
case 4:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function(_x, _x2, _x3) {
return _ref.apply(this, arguments);
};
})()
);
};
var filterResultsReducer = function filterResultsReducer(filteredArray, _ref2) {
var filterResult = _ref2.filterResult,
element = _ref2.element;
if (filterResult) {
filteredArray.push(element);
}
return filteredArray;
};
var getFilteredInParallel =
/*#__PURE__*/
(function() {
var _ref3 = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee2(filterFn, array) {
var filterMergeMapFn, filterValues;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch ((_context2.prev = _context2.next)) {
case 0:
filterMergeMapFn = filterMergeMap(filterFn);
_context2.next = 3;
return Promise.all(array.map(filterMergeMapFn));
case 3:
filterValues = _context2.sent;
return _context2.abrupt("return", filterValues.reduce(filterResultsReducer, []));
case 5:
case "end":
return _context2.stop();
}
}
}, _callee2);
})
);
return function getFilteredInParallel(_x4, _x5) {
return _ref3.apply(this, arguments);
};
})();
var getFilteredInSequence =
/*#__PURE__*/
(function() {
var _ref4 = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee3(filterFn, array) {
var result, i, filterResult;
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch ((_context3.prev = _context3.next)) {
case 0:
result = [];
i = 0;
case 2:
if (!(i < array.length)) {
_context3.next = 10;
break;
}
_context3.next = 5;
return filterFn(array[i], i, array);
case 5:
filterResult = !!_context3.sent;
if (filterResult) {
result.push(array[i]);
}
case 7:
i++;
_context3.next = 2;
break;
case 10:
return _context3.abrupt("return", result);
case 11:
case "end":
return _context3.stop();
}
}
}, _callee3);
})
);
return function getFilteredInSequence(_x6, _x7) {
return _ref4.apply(this, arguments);
};
})();
var afilterGeneric = function() {
var sequence = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
return function(filterFn) {
return (
/*#__PURE__*/
(function() {
var _ref5 = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee4(iterable) {
var sourceArray, array;
return regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch ((_context4.prev = _context4.next)) {
case 0:
_context4.next = 2;
return extractArrayFromArgument(iterable);
case 2:
sourceArray = _context4.sent;
_context4.next = 5;
return Promise.all(sourceArray);
case 5:
array = _context4.sent;
if (!sequence) {
_context4.next = 12;
break;
}
_context4.next = 9;
return getFilteredInSequence(filterFn, array);
case 9:
_context4.t0 = _context4.sent;
_context4.next = 15;
break;
case 12:
_context4.next = 14;
return getFilteredInParallel(filterFn, array);
case 14:
_context4.t0 = _context4.sent;
case 15:
return _context4.abrupt("return", _context4.t0);
case 16:
case "end":
return _context4.stop();
}
}
}, _callee4);
})
);
return function(_x8) {
return _ref5.apply(this, arguments);
};
})()
);
};
};
/**
* An asynchronous version of filter over an iterable (afilter stays for async-filter).
*
* It gets an iterable of values (or promises) as input (or promise to resolve to iterable),
* resolves them, iterates over them with filter function
* (which returns boolean where true means current value will be included in resulting array)
* and returns a promise which resolves to an array of values (filtered input iterable).
*
* It allows asynchronous filtering point-free way and can be used with asynchronous compose functions.
*
* It uses Promise.all() under the hood.
* So if filtering function is asynchronous (returns a promise) all promises are being generated at once
* and then resolved with Promise.all().
* So if any of promises will produce error (promise rejection) all the other promises will be invoked anyway.
* The advantage of this method of invoking promises it will finish earlier than sequential filter (because of Promise.all())
* but it may perform some fetches or even state modifications even in case of fail on some previous filtering steps.
*
* <pre><code>const [ first, third ] = await afilter(fetchPermissions)([somePromise1, someValue2, somePromise3]);</code></pre>
*
* It first resolves a promises passed and then pass resolutions value to the filtering function.
*
* Input iterable's values are not restricted to promises but can be any value to pass as input to functions.
*
* It also allows to handle errors like for traditional Promise:
*
* <pre><code>afilter(fetchPermissions)(somePromise1, someValue2, somePromise3).catch(e => console.error(e));</code></pre>
*
* @param {function} filterFn Is filtering function which can produce a promise (but not restricted to this).
* Function can return a promise (asynchronous filtering) or may just perform some synchronous filtering.
* So you can use it in synchronous code taking in mind it returns promise so can't be resolved immediately.
* It has three parameters (currentValue, currentIndex, array) which are already resolved (not promises).
* @returns {(iterable : Promise|Iterable.<*>) => Promise} A function which expects any values as input (resolving to Promise)
* and returns a Promise.
*/
var afilter = afilterGeneric();
/**
* An asynchronous version of filter over an iterable (afilterSeq stays for async-filter).
*
* It gets an iterable of values (or promises) as input (or promise to resolve to iterable),
* resolves them, iterates over them with filter function
* (which returns boolean where true means current value will be included in resulting array)
* and returns a promise which resolves to an array of values (filtered input iterable).
*
* It allows asynchronous filtering point-free way and can be used with asynchronous compose functions.
*
* The difference from regular afilter is if filter function is asynchronous (returns a promise)
* every new invocation of filter function performs sequentially after resolving previous promise.
* So if any of promises produces error (promise rejection) afilterSeq will not produce new promises and they won't be invoked.
*
* <pre><code>const [ first, third ] = await afilterSeq(fetchPermissions)([somePromise1, someValue2, somePromise3]);</code></pre>
*
* It first resolves a promises passed and then pass resolutions value to the filtering function.
*
* Input iterable's values are not restricted to promises but can be any value to pass as input to functions.
*
* It also allows to handle errors like for traditional Promise:
*
* <pre><code>afilterSeq(fetchPermissions)(somePromise1, someValue2, somePromise3).catch(e => console.error(e));</code></pre>
*
* @param {function} filterFn Is filtering function which can produce a promise (but not restricted to this).
* Function can return a promise (asynchronous filtering) or may just perform some synchronous filtering.
* So you can use it in synchronous code taking in mind it returns promise so can't be resolved immediately.
* It has three parameters (currentValue, currentIndex, array) which are already resolved (not promises).
* @returns {(iterable : Promise|Iterable.<*>) => Promise} A function which expects any values as input (resolving to Promise)
* and returns a Promise.
*/
var afilterSeq = afilterGeneric(true);
var getMappedInParallel = function getMappedInParallel(mapFn, array) {
return Promise.all(array.map(mapFn));
};
var getMappedInSequence =
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(mapFn, array) {
var result, i, mapResult;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
result = [];
i = 0;
case 2:
if (!(i < array.length)) {
_context.next = 10;
break;
}
_context.next = 5;
return mapFn(array[i], i, array);
case 5:
mapResult = _context.sent;
result.push(mapResult);
case 7:
i++;
_context.next = 2;
break;
case 10:
return _context.abrupt("return", result);
case 11:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function getMappedInSequence(_x, _x2) {
return _ref.apply(this, arguments);
};
})();
var amapGeneric = function() {
var sequence = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
return function(mapFn) {
return (
/*#__PURE__*/
(function() {
var _ref2 = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee2(iterable) {
var sourceArray, array;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch ((_context2.prev = _context2.next)) {
case 0:
_context2.next = 2;
return extractArrayFromArgument(iterable);
case 2:
sourceArray = _context2.sent;
_context2.next = 5;
return Promise.all(sourceArray);
case 5:
array = _context2.sent;
return _context2.abrupt(
"return",
sequence
? getMappedInSequence(mapFn, array)
: getMappedInParallel(mapFn, array)
);
case 7:
case "end":
return _context2.stop();
}
}
}, _callee2);
})
);
return function(_x3) {
return _ref2.apply(this, arguments);
};
})()
);
};
};
/**
* An asynchronous version of map over an iterable (amap stays for async-map).
*
* It gets an iterable of values (or promises) as input (or promise to resolve to iterable),
* resolves them, maps over map function and returns a promise which resolves to an array of values.
*
* It allows asynchronous mapping point-free way and can be used with asynchronous compose functions.
*
* It uses Promise.all() under the hood.
* So if mapping function is asynchronous (returns a promise) all promises are being generated at once
* and then resolved with Promise.all().
* So if any of promises will produce error (promise rejection) all the other promises will be invoked.
* The advantage of this method of invoking promises it will finish earlier than sequential map (because of Promise.all())
* but it may perform some fetches or even state modifications even in case of fail on some previous mapping steps.
*
* <pre><code>const [ first, second, third ] = await amap(getDataFromServer)([somePromise1, someValue2, somePromise3]);</code></pre>
*
* It first resolves a promises passed and then pass resolutions value to the mapping function.
*
* Input iterable's values are not restricted to promises but can be any value to pass as input to functions.
*
* It also allows to handle errors like for traditional Promise:
*
* <pre><code>amap(getDataFromServer)(somePromise1, someValue2, somePromise3).catch(e => console.error(e));</code></pre>
*
* @param {function} mapFn Is mapping function which can produce a promise (but not restricted to this).
* Function can return a promise (asynchronous mapping) or may just perform some synchronous mapping.
* So you can use it in synchronous code taking in mind it returns promise so can't be resolved immediately.
* It has three parameters (currentValue, currentIndex, array) which are resolved (not promises).
* @returns {(iterable : Promise|Iterable.<*>) => Promise} A function which expects any values as input (resolving to Promise)
* and returns a Promise.
*/
var amap = amapGeneric();
/**
* An asynchronous version of map over an iterable (amap stays for async-map).
*
* It gets an iterable of values (or promises) as input (or promise to resolve to iterable),
* resolves them, maps over map function and returns a promise which resolves to an array of values.
*
* It allows asynchronous mapping point-free way and can be used with asynchronous compose functions.
*
* The difference from regular amap is if map function is asynchronous (returns a promise)
* every new invocation of map function performs sequentially after resolving previous promise.
* So if any of promises produces error (promise rejection) amapSeq will not produce new promises and they won't be invoked.
*
* <pre><code>const [ first, second, third ] = await amap(getDataFromServer)([somePromise1, someValue2, somePromise3]);</code></pre>
*
* It first resolves a promises passed and then pass resolutions value to the mapping function.
*
* Input iterable's values are not restricted to promises but can be any value to pass as input to functions.
*
* It also allows to handle errors like for traditional Promise:
*
* <pre><code>amap(getDataFromServer)(somePromise1, someValue2, somePromise3).catch(e => console.error(e));</code></pre>
*
* @param {function} mapFn Is mapping function which can produce a promise (but not restricted to this).
* Function can return a promise (asynchronous mapping) or may just perform some synchronous mapping.
* So you can use it in synchronous code taking in mind it returns promise so can't be resolved immediately.
* It has three parameters (currentValue, currentIndex, array) which are resolved (not promises).
* @returns {(iterable : Promise|Iterable.<*>) => Promise} A function which expects any values as input (resolving to Promise)
* and returns a Promise.
*/
var amapSeq = amapGeneric(true);
/**
* Asynchronous pipe function (apipe stays for async-pipe).
*
* The main purpose is to replace a Promise handling code like this:
* <pre><code>somePromise.then(normalize).then(upperCase).then(insertGreetings);</code></pre>
*
* with point-free style of functional pipe syntax like the following:
* <pre><code>apipe(normalize, upperCase, insertGreetings)(somePromise);</code></pre>
*
* It is lazy and allows of reusing of promise handling chains.
*
* You can run apipe with Promise instance (for true asynchronous execution)
* or with any other object to use as in usual functional composition.
* It produces a Promise and can be used in async/await context:
*
* <pre><code>const message = await apipe(normalize, upperCase, insertGreetings)(somePromise);</code></pre>
*
* It also allows to handle errors like for traditional Promise but only in the tail position of the chain:
*
* <pre><code>apipe(normalize, upperCase, insertGreetings)(somePromise).catch(e => console.error(e));</code></pre>
*
* @param {...function|Iterable.<*>} fns Are functions to pipe chains of promises.
* @returns {(promise : Promise|any) => Promise} A function which expects any value as input (resolving to Promise) and returns a Promise.
*/
var apipe = function() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
return (
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(promise) {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
return _context.abrupt(
"return",
extractResolvedArguments(fns).reduce(function(promise, fn) {
var promiseHandler = supportsCustomPromiseHandling(fn);
if (promiseHandler) {
return promiseHandler(promise);
}
return promise.then(fn);
}, Promise.resolve(promise))
);
case 1:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function(_x) {
return _ref.apply(this, arguments);
};
})()
);
};
/**
* A kind of composable version of Promise.all().
*
* It gets some value or promise as input, pass it to the functions list
* and produces the array of results after resolving all the functions which can return promises as well.
*
* It allows to use Promise.all() point-free way.
*
* <pre><code>const [ first, second ] = await applyFns(squareRoot, getDataFromServer)(somePromise);</code></pre>
*
* It first resolves a promise passed and then pass resolution value to all the functions.
*
* Input value is not restricted to promise but can be any value to pass as input to functions.
*
* It also allows to handle errors like for traditional Promise:
*
* <pre><code>applyFns(squareRoot, getDataFromServer)(somePromise).catch(e => console.error(e));</code></pre>
*
* @param {...function|Iterable.<*>} fns Are functions to handle input value in parallel.
* Functions can return promises or may just perform some mapping.
* So you can use it in synchronous code taking in mind it returns promise so can't be resolved immediately.
* @returns {(value : Promise|any) => Promise} A function which expects any value as input (resolving to Promise) and returns a Promise.
*/
var applyFns = function() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
return (
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(value) {
var resolvedValue;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_context.next = 2;
return Promise.resolve(value);
case 2:
resolvedValue = _context.sent;
return _context.abrupt(
"return",
Promise.all(
extractResolvedArguments(fns).map(function(fn) {
return fn(resolvedValue);
})
)
);
case 4:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function(_x) {
return _ref.apply(this, arguments);
};
})()
);
};
/**
* Composable version of promise.then(mapFn).catch(catchFn).
*
* It gets a value (a promise or not), resolves it and handles as promise.then(mapFn).catch(catchFn) returning resulting promise.
*
* It allows to handle errors within acompose or apipe asynchronous composition chains to restore broken state etc.
*
* A sample with acompose:
*
* <pre><code>const resultOrFallback = await acompose(applySafe(canFailFn, handleAndRecoverFn), canFailTooFn)(someInput);</code></pre>
*
* Standalone usage:
*
* <pre><code>const resultOrFallback = await applySafe(canFailFn, handleAndRecoverFn)(requestDataAndReturnPromise());</code></pre>
*
* Or even:
*
* <pre><code>const resultOrFallback = await applySafe(acompose(handlerFn2, handlerFn1), handleAndRecoverFn)(requestDataAndReturnPromise());</code></pre>
*
* It is the same as the following:
*
* <pre><code>requestDataAndReturnPromise().then(canFailFn).catch(handleAndRecoverFn).then(resultOrFallback => console.log(resultOrFallback));</code></pre>
*
* @param {function} mapFn Is function to handle Promise's resolution (then).
* @param {function} catchFn Is function to handle Promise's rejection (catch).
* @returns {any => Promise} A function which expects any value as input (Promise or not) and returns a Promise.
*/
var applySafe = function(mapFn, catchFn) {
var handler = function handler(value) {
return Promise.resolve(value)
.then(mapFn)
.catch(catchFn);
};
return addCustomPromiseHandlingSupport(handler, function(promise) {
return promise.then(mapFn).catch(catchFn);
});
};
var resolveArrayFromInput =
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(iterable) {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_context.t0 = Promise;
_context.next = 3;
return extractArrayFromArgument(iterable);
case 3:
_context.t1 = _context.sent;
return _context.abrupt("return", _context.t0.all.call(_context.t0, _context.t1));
case 5:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function resolveArrayFromInput(_x) {
return _ref.apply(this, arguments);
};
})();
var reducer = function reducer(reduceFn) {
return function(acc, current, index, array) {
return Promise.resolve(acc).then(function(acc) {
return reduceFn(acc, current, index, array);
});
};
};
var getReducerArguments = function getReducerArguments(args) {
var effectiveReduceFn = reducer(args[0]);
var effectiveArguments = _toConsumableArray(args);
effectiveArguments[0] = effectiveReduceFn;
return effectiveArguments;
};
/**
* Asynchronous composable version of reduce method for iterables ("a" stays for "asynchronous").
*
* It gets a list of values (or list of promises, or promise to resolve to list) and performs standard reduce on them.
*
* Reduce function may be asynchronous to return a promise (to fetch some data etc).
*
* Initial value of reducer also could be a promise.
*
* A sample usage is:
*
* <pre><code>const sum = async (currentSum, invoiceId) => {
* const { total:invoiceTotal } = await fetchInvoiceById(invoiceId);
* return currentSum + invoiceTotal;
* };
* const paymentTotal = await areduce(sum, 0)(fetchInvoiceIds(userId));</code></pre>
*
* Or the same with acompose:
*
* <pre><code>const paymentTotal = await acompose(areduce(sum, 0), fetchInvoiceIds)(userId);</code></pre>
*
* @param {function} callback Function to execute on each element in the array, taking four arguments
* (accumulator, currentValue, currentIndex, array).
* @param {any} initialValue (optional) Value to use as the first argument to the first call of the callback.
* @returns {(iterable : Promise|Iterable.<*>) => Promise} A function which expects an iterable
* (or promise resolved to iterable) and returns a Promise.
*/
var areduce = function() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return (
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(iterable) {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_context.t0 = Array.prototype.reduce;
_context.next = 3;
return resolveArrayFromInput(iterable);
case 3:
_context.t1 = _context.sent;
_context.t2 = getReducerArguments(args);
return _context.abrupt(
"return",
_context.t0.apply.call(_context.t0, _context.t1, _context.t2)
);
case 6:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function(_x) {
return _ref.apply(this, arguments);
};
})()
);
};
/**
* Asynchronous composable version of reduceRight method for iterables ("a" stays for "asynchronous").
*
* It gets a list of values (or list of promises, or promise to resolve to list) and performs standard reduce on them.
*
* Reduce function may be asynchronous to return a promise (to fetch some data etc).
*
* Initial value of reducer also could be a promise.
*
* A sample usage is:
*
* <pre><code>const sum = async (currentSum, invoiceId) => {
* const { total:invoiceTotal } = await fetchInvoiceById(invoiceId);
* return currentSum + invoiceTotal;
* };
* const paymentTotal = await areduceRight(sum, 0)(fetchInvoiceIds(userId));</code></pre>
*
* Or the same with acompose:
*
* <pre><code>const paymentTotal = await acompose(areduceRight(sum, 0), fetchInvoiceIds)(userId);</code></pre>
*
* @param {function} callback Function to execute on each element in the array, taking four arguments
* (accumulator, currentValue, currentIndex, array).
* @param {any} initialValue (optional) Value to use as the first argument to the first call of the callback.
* @returns {(iterable : Promise|Iterable.<*>) => Promise} A function which expects an iterable
* (or promise resolved to iterable) and returns a Promise.
*/
var areduceRight = function() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return (
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(iterable) {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
_context.t0 = Array.prototype.reduceRight;
_context.next = 3;
return resolveArrayFromInput(iterable);
case 3:
_context.t1 = _context.sent;
_context.t2 = getReducerArguments(args);
return _context.abrupt(
"return",
_context.t0.apply.call(_context.t0, _context.t1, _context.t2)
);
case 6:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function(_x) {
return _ref.apply(this, arguments);
};
})()
);
};
exports.acatch = acatch;
exports.acompose = acompose;
exports.afilter = afilter;
exports.afilterSeq = afilterSeq;
exports.amap = amap;
exports.amapSeq = amapSeq;
exports.apipe = apipe;
exports.applyFns = applyFns;
exports.applySafe = applySafe;
exports.areduce = areduce;
exports.areduceRight = areduceRight;
Object.defineProperty(exports, "__esModule", { value: true });
});
//# sourceMappingURL=fun-ctional.js.map