Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/constantiner/fun-ctional
The library brings most of the familiar functional techniques (like functional composition) to asynchronous world with shining Promises
https://github.com/constantiner/fun-ctional
async asynchronous asynchronous-functions asynchronous-programming client-side functional functional-composition functional-programming javascript javascript-library library mapreduce nodejs promise promise-handling promises server-side
Last synced: about 2 months ago
JSON representation
The library brings most of the familiar functional techniques (like functional composition) to asynchronous world with shining Promises
- Host: GitHub
- URL: https://github.com/constantiner/fun-ctional
- Owner: Constantiner
- License: mit
- Created: 2018-05-28T14:06:04.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2023-01-04T21:36:42.000Z (about 2 years ago)
- Last Synced: 2024-12-06T12:48:03.851Z (about 2 months ago)
- Topics: async, asynchronous, asynchronous-functions, asynchronous-programming, client-side, functional, functional-composition, functional-programming, javascript, javascript-library, library, mapreduce, nodejs, promise, promise-handling, promises, server-side
- Language: JavaScript
- Homepage:
- Size: 856 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 21
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# fun-ctional
[![Build Status](https://travis-ci.org/Constantiner/fun-ctional.svg?branch=master)](https://travis-ci.org/Constantiner/fun-ctional) [![codecov](https://codecov.io/gh/Constantiner/fun-ctional/branch/master/graph/badge.svg)](https://codecov.io/gh/Constantiner/fun-ctional)
With `fun-ctional` library it is possible to use most of the familiar functional techniques (like functional composition) in asynchronous world with shining Promises.
It allows to mix synchronous and asynchronous functions to produce reusable composable functions which compatible with all good old utilities from functional libraries like [Lodash](https://lodash.com/) in terms of ability to use any synchronous functions. The only difference is that functions from `fun-ctional` library always return promises.
- [Changelog](#changelog)
- [Installation](#installation)
- [Documentation](#documentation)
- [acompose](#acompose)
- [apipe](#apipe)
- [amap](#amap)
- [amapSeq](#amapseq)
- [areduce](#areduce)
- [areduceRight](#areduceright)
- [afilter](#afilter)
- [afilterSeq](#afilterseq)
- [applyFns](#applyfns)
- [acatch](#acatch)
- [applySafe](#applysafe)## Changelog
Versions [changelog](docs/CHANGELOG.md).
## Installation
Install it with NPM:
`npm install @constantiner/fun-ctional`
**Note**. The UMD version of package is ES5 compatible but you may need to use [`@babel/polyfill`](https://babeljs.io/docs/en/babel-polyfill/) (for `Symbol` support) or even [`@babel/runtime`](https://babeljs.io/docs/en/babel-runtime) (`async`/`await` support) with it.
**Note**. Build includes sourcemaps and minified versions of UMD files as well. You can find them in your `node_modules/@constantiner/fun-ctional/browser` folder.
## Documentation
At this moment the following utilities are available:
### acompose
Asynchronous compose function (`acompose` stays for async-compose).
The main purpose is to replace a Promise handling code like this:
```JavaScript
somePromise.then(normalize).then(upperCase).then(insertGreetings);
```with point-free style of functional compose syntax like the following:
```JavaScript
acompose(insertGreetings, upperCase, normalize)(somePromise);
```It is lazy and allows of reusing of promise handling chains.
First you need to import it:
```JavaScript
import { acompose } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { acompose } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import acompose from "@constantiner/fun-ctional/acompose";
```Or:
```JavaScript
const acompose = require("@constantiner/fun-ctional/acompose");
```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:
```JavaScript
const message = await acompose(insertGreetings, upperCase, normalize)(somePromise);
```It also allows to handle errors like for traditional Promise but only in the tail position of the chain:
```JavaScript
acompose(insertGreetings, upperCase, normalize)(somePromise).catch(e => console.error(e));
```### apipe
Asynchronous pipe function (`apipe` stays for async-pipe).
The main purpose is to replace a Promise handling code like this:
```JavaScript
somePromise.then(normalize).then(upperCase).then(insertGreetings);
```with point-free style of functional pipe syntax like the following:
```JavaScript
apipe(normalize, upperCase, insertGreetings)(somePromise);
```It is lazy and allows of reusing of promise handling chains.
First you need to import it:
```JavaScript
import { apipe } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { apipe } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import apipe from "@constantiner/fun-ctional/apipe";
```Or:
```JavaScript
const apipe = require("@constantiner/fun-ctional/apipe");
```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:
```JavaScript
const message = await apipe(normalize, upperCase, insertGreetings)(somePromise);
```It also allows to handle errors like for traditional Promise but only in the tail position of the chain:
```JavaScript
apipe(normalize, upperCase, insertGreetings)(somePromise).catch(e => console.error(e));
```### amap
An asynchronous version of map over an iterable (amap stays for async-map).
It gets an iterable of values (or promises) as input (or a 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.
See [`amapSeq`](#amapseq) for sequential implementation.
```JavaScript
const [ first, second, third ] = await amap(getDataFromServer)([somePromise1, someValue2, somePromise3]);
```Or even more traditional way:
```JavaScript
amap(getDataFromServer)([somePromise1, someValue2, somePromise3])
.then(values => console.log(values));
```It first resolves a promises passed and then pass resolutions value to the mapping function.
Mapping function is called with three parameters: `currentValue`, `currentIndex`, `array` which are plain resolved values (not promises).
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:
```JavaScript
amap(getDataFromServer)([somePromise1, someValue2, somePromise3]).catch(e => console.error(e));
```Or you can use `try/catch` in `async/await` constructions.
Нou can use it with [`acompose`](#acompose) or [`apipe`](#apipe):
```JavaScript
const usersHtml = await acompose(getHtmlRepresentation, getUserNames, amap(getUser), getUserIds)(somePromise);
```You can import it the following way:
```JavaScript
import { amap } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { amap } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import amap from "@constantiner/fun-ctional/amap";
```Or:
```JavaScript
const amap = require("@constantiner/fun-ctional/amap");
```### amapSeq
An asynchronous version of map over an iterable (amapSeq stays for async-map).
It gets an iterable of values (or promises) as input (or a 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`](#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.
See [`amap`](#amap) for parallel implementation.
```JavaScript
const [ first, second, third ] = await amapSeq(getDataFromServer)([somePromise1, someValue2, somePromise3]);
```Or even more traditional way:
```JavaScript
amapSeq(getDataFromServer)([somePromise1, someValue2, somePromise3])
.then(values => console.log(values));
```It first resolves a promises passed and then pass resolutions value to the mapping function.
Mapping function is called with three parameters: `currentValue`, `currentIndex`, `array` which are plain resolved values (not promises).
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:
```JavaScript
amapSeq(getDataFromServer)([somePromise1, someValue2, somePromise3]).catch(e => console.error(e));
```Or you can use `try/catch` in `async/await` constructions.
Нou can use it with [`acompose`](#acompose) or [`apipe`](#apipe):
```JavaScript
const usersHtml = await acompose(getHtmlRepresentation, getUserNames, amapSeq(getUser), getUserIds)(somePromise);
```You can import it the following way:
```JavaScript
import { amapSeq } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { amapSeq } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import amapSeq from "@constantiner/fun-ctional/amapSeq";
```Or:
```JavaScript
const amapSeq = require("@constantiner/fun-ctional/amapSeq");
```### areduce
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:
```JavaScript
const sum = async (currentSum, invoiceId) => {
const { total:invoiceTotal } = await fetchInvoiceById(invoiceId);
return currentSum + invoiceTotal;
};const paymentTotal = await areduce(sum, 0)(fetchInvoiceIds(userId));
```Or the same with [`acompose`](#acompose):
```JavaScript
const paymentTotal = await acompose(areduce(sum, 0), fetchInvoiceIds)(userId);
```It takes a standard callback Function to execute on each element in the array, taking four standard arguments (`accumulator`, `currentValue`, `currentIndex`, `array`) and returns a function to accept input value (so it is composable).
You can import it the following way:
```JavaScript
import { areduce } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { areduce } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import areduce from "@constantiner/fun-ctional/areduce";
```Or:
```JavaScript
const areduce = require("@constantiner/fun-ctional/areduce");
```### areduceRight
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:
```JavaScript
const sum = async (currentSum, invoiceId) => {
const { total:invoiceTotal } = await fetchInvoiceById(invoiceId);
return currentSum + invoiceTotal;
};const paymentTotal = await areduceRight(sum, 0)(fetchInvoiceIds(userId));
```Or the same with [`acompose`](#acompose):
```JavaScript
const paymentTotal = await acompose(areduceRight(sum, 0), fetchInvoiceIds)(userId);
```It takes a standard callback Function to execute on each element in the array, taking four standard arguments (`accumulator`, `currentValue`, `currentIndex`, `array`) and returns a function to accept input value (so it is composable).
You can import it the following way:
```JavaScript
import { areduceRight } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { areduceRight } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import areduceRight from "@constantiner/fun-ctional/areduceRight";
```Or:
```JavaScript
const areduceRight = require("@constantiner/fun-ctional/areduceRight");
```### afilter
An asynchronous version of filter over an iterable (afilter stays for async-filter).
It gets an iterable of values (or promises) as input (or a promise to resolve to iterable), resolves them, filters over 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.
See [`afilterSeq`](#afilterseq) for sequential implementation.
```JavaScript
const [ first, third ] = await afilter(fetchPermissions)([somePromise1, someValue2, somePromise3]);
```Or even more traditional way:
```JavaScript
afilter(fetchPermissions)([somePromise1, someValue2, somePromise3])
.then(values => console.log(values));
```It first resolves a promises passed and then pass resolutions value to the filtering function.
Filtering function is called with three parameters: `currentValue`, `currentIndex`, `array` which are plain resolved values (not promises) and expects `Boolean` as return value (or a promise resolved to `Boolean`).
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:
```JavaScript
afilter(fetchPermissions)([somePromise1, someValue2, somePromise3]).catch(e => console.error(e));
```Or you can use `try/catch` in `async/await` constructions.
Нou can use it with [`acompose`](#acompose) or [`apipe`](#apipe):
```JavaScript
const usersHtml = await acompose(getHtmlRepresentation, getUserNames, afilter(fetchPermissions), getUserIds)(somePromise);
```You can import it the following way:
```JavaScript
import { afilter } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { afilter } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import afilter from "@constantiner/fun-ctional/afilter";
```Or:
```JavaScript
const afilter = require("@constantiner/fun-ctional/afilter");
```### afilterSeq
An asynchronous version of filter over an iterable (afilterSeq stays for async-filter).
It gets an iterable of values (or promises) as input (or a promise to resolve to iterable), resolves them, filters over 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`](#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.
See [`afilter`](#afilter) for parallel implementation.
```JavaScript
const [ first, third ] = await afilterSeq(fetchPermissions)([somePromise1, someValue2, somePromise3]);
```Or even more traditional way:
```JavaScript
afilterSeq(fetchPermissions)([somePromise1, someValue2, somePromise3])
.then(values => console.log(values));
```It first resolves a promises passed and then pass resolutions value to the filtering function.
Filtering function is called with three parameters: `currentValue`, `currentIndex`, `array` which are plain resolved values (not promises) and expects `Boolean` as return value (or a promise resolved to `Boolean`).
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:
```JavaScript
afilterSeq(fetchPermissions)([somePromise1, someValue2, somePromise3]).catch(e => console.error(e));
```Or you can use `try/catch` in `async/await` constructions.
Нou can use it with [`acompose`](#acompose) or [`apipe`](#apipe):
```JavaScript
const usersHtml = await acompose(getHtmlRepresentation, getUserNames, afilterSeq(fetchPermissions), getUserIds)(somePromise);
```You can import it the following way:
```JavaScript
import { afilterSeq } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { afilterSeq } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import afilterSeq from "@constantiner/fun-ctional/afilterSeq";
```Or:
```JavaScript
const afilterSeq = require("@constantiner/fun-ctional/afilterSeq");
```### applyFns
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:
```JavaScript
const [ first, second ] = await applyFns(squareRoot, getDataFromServer)(somePromise);
```Or:
```JavaScript
const [ first, second ] = await applyFns(squareRoot, getDataFromServer)(25);
```Or some more traditional way:
```JavaScript
applyFns(squareRoot, getDataFromServer)(somePromise)
.then(([ first, second ]) => [ second, first ]);
```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:
```JavaScript
applyFns(squareRoot, getDataFromServer)(somePromise).catch(e => console.error(e));
```or the same with async/await.
You can use it with [`acompose`](#acompose) or [`apipe`](#apipe):
```JavaScript
const userHtml = await acompose(getHtmlRepresentation, getFullName, applyFns(getFirstNameById, getLastNameById), getUserId)(somePromise);
```You can import it the following way:
```JavaScript
import { applyFns } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { applyFns } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import applyFns from "@constantiner/fun-ctional/applyFns";
```Or:
```JavaScript
const applyFns = require("@constantiner/fun-ctional/applyFns");
```### acatch
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 passed.
It allows to handle errors within [`acompose`](#acompose) or [`apipe`](#apipe) asynchronous composition chains to restore broken state etc.
See [`applySafe`](#applysafe) for an option to invoke catch handler along with some mapping function.
A sample with [`acompose`](#acompose):
```JavaScript
const resultOrFallback = await acompose(acatch(handleAndRecoverFn), canFailFn)(someInput);
```Standalone usage:
```JavaScript
const resultOrFallback = await acatch(handleAndRecoverFn)(requestDataAndReturnPromise());
```It is the same as the following:
```JavaScript
requestDataAndReturnPromise().catch(handleAndRecoverFn).then(resultOrFallback => console.log(resultOrFallback));
```You can import it the following way:
```JavaScript
import { acatch } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { acatch } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import acatch from "@constantiner/fun-ctional/acatch";
```Or:
```JavaScript
const acatch = require("@constantiner/fun-ctional/acatch");
```### applySafe
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`](#acompose) or [`apipe`](#apipe) asynchronous composition chains to restore broken state etc.
See [`acatch`](#acatch) for composable invocation of catch handler separately.
A sample with [`acompose`](#acompose):
```JavaScript
const resultOrFallback = await acompose(applySafe(canFailFn, handleAndRecoverFn), canFailTooFn)(someInput);
```Standalone usage:
```JavaScript
const resultOrFallback = await applySafe(canFailFn, handleAndRecoverFn)(requestDataAndReturnPromise());
```Here `canFailFn` is replacement for standard Promise's `then` method (which can reject) and `handleAndRecoverFn` for Promise's `catch`.
It is the same as the following:
```JavaScript
requestDataAndReturnPromise().then(canFailFn).catch(handleAndRecoverFn).then(resultOrFallback => console.log(resultOrFallback));
```Or even more complex example:
```JavaScript
const resultOrFallback = await applySafe(acompose(handlerFn2, handlerFn1), handleAndRecoverFn)(requestDataAndReturnPromise());
```You can import it the following way:
```JavaScript
import { applySafe } from "@constantiner/fun-ctional";
```Or:
```JavaScript
const { applySafe } = require("@constantiner/fun-ctional");
```Or you can import it separately without the whole bundle:
```JavaScript
import applySafe from "@constantiner/fun-ctional/applySafe";
```Or:
```JavaScript
const applySafe = require("@constantiner/fun-ctional/applySafe");
```