UNPKG

@constantiner/fun-ctional

Version:

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

259 lines (229 loc) 8.18 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 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(); return amap; }); //# sourceMappingURL=amap.js.map