UNPKG

@constantiner/fun-ctional

Version:

The library brings most of the familiar functional techniques (like functional composition) to asynchronous world with shining Promises

359 lines (312 loc) 10.7 kB
/** * @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" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : ((global = global || self), (global.funCtional = factory())); })(this, function() { "use strict"; 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); }); }; } /* 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(); return afilter; }); //# sourceMappingURL=afilter.js.map