@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
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"
? (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