A js transducers-like implementation using generators and ES8 async generators.
A composed function expecting a combination function to make a reducer is called transducer. Transducers are useful to compose adjacent map(), filer() and reduce() operations together to improve performaces.
A jducer is similar to a transducer because it is a composed function and can be used to compose map(), filer() and reduce() operations together, but there are some differences.
It was a challenge against myself to see how far could I go using generators.
$ npm i --save jducersA little fp utility library used by jducers and available for you with pipe, compose, curry, partial and partialRight
import{pipe,compose,curry,partial,partialRight}from'jducers/src/utility';Sync's jducers helpers:
import{map,filter,reduce,run}from'jducers/src/jducers/sync'or
import*asSJfrom'jducers/src/jducers/sync'- map: accepts only a mapper function that is up to you and returns a function used in the creation of a jducer
- filter: accepts only a predicate function that is up to you and returns a function used in the creation of a jducer
- reduce: accepts two parameters: a reducer function with some constraints and an optional initial value and returns a function used in the creation of a jducer. The reducer function will be called with only two parameters: the accumulator and the current processed value
- run: accepts two parameters: a composition (you can use pipe or compose from my utility library) and a sync iterable like an array. Returns the resuling array or a single value.It depends on the functions used in the composition
import*asSJfrom'jducers/src/jducers/sync'import{pipe,partialRight}from'jducers/src/utility';letarray=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];constisOdd=i=>!!(i%2);// predicate functionconstdouble=i=>i*2;// mapper functionconstsum=(acc,val)=>acc+val;// reducerconstsyncIsOddFilter=SJ.filter(isOdd);constsyncDoubleMap=SJ.map(double);constsyncSumReduce=SJ.reduce(sum);construn=partialRight(SJ.run,array);letjducer=pipe(syncIsOddFilter,syncDoubleMap);letres=run(jducer);console.log(res);// [2, 6, 10, 14, 18, 22, 26, 30, 34, 38]jducer=pipe(syncIsOddFilter,syncDoubleMap,syncSumReduce);res=run(jducer);console.log(res);// 200Async's jducers helpers (useful for async iterables and concurrent async iterations):
import{map,filter,reduce,run,observerFactory}from'jducers/src/jducers/async'or
import*asAJfrom'jducers/src/jducers/async'- map: accepts only a mapper function that is up to you and returns a function used in the creation of a jducer
- filter: accepts only a predicate function that is up to you and returns a function used in the creation of a jducer
- reduce: accepts two parameters: a reducer function with some constraints and an optional initial value and returns a function used in the creation of a jducer. The reducer function will be called with only two parameters: the accumulator and the current processed value
- run: accepts two parameters: a composition (you can use pipe or compose from my utility library) and an async iterable like an array of promises. Returns a promise that will be fulfilled with the resulting array or a single value. It depends on the functions used in the composition
- observerFactory: accepts one or more callbacks and returns a simple observer that calls them when each single value of our async iterable flows through it. The observer has to be placed in a composition to form the jducer
import*asAJfrom'jducers/src/jducers/async'import{pipe,partialRight}from'jducers/src/utility';constasyncArray={array: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],[Symbol.asyncIterator]: asyncfunction*(){for(constxofthis.array){awaitnewPromise(ok=>setTimeout(()=>ok(x),2000));yieldx;}}}constisOdd=i=>!!(i%2);// predicate functionconstdouble=i=>i*2;// mapper functionconstsum=(acc,val)=>acc+val;// reducerconstasyncIsOddFilter=AJ.filter(isOdd);constasyncDoubleMap=AJ.map(double);constasyncSumReduce=AJ.reduce(sum);construn=partialRight(AJ.run,asyncArray);letjducer=pipe(asyncIsOddFilter,asyncDoubleMap);letres=run(jducer);res.then(x=>console.log(x));// [2, 6, 10, 14, 18, 22, 26, 30, 34, 38]jducer=pipe(asyncIsOddFilter,asyncDoubleMap,asyncSumReduce);res=run(jducer);res.then(x=>console.log(x));// 200constobserver=AJ.observerFactory(console.log);/* const observer = observerFactory(); observer.add(console.log);*/jducer=pipe(observer,asyncDoubleMap,observer,asyncSumReduce);// we will see each value before and after the double mapper function// 1 2 2 4 3 6 4 8 5 10 6 12 ...res=run(jducer);res.then(x=>console.log(x));// 420// WARNING: OUTPUTS ARE IN CONCURRENCYAll modules and functions together are 3.284Kb without compression
Bad, because yield is still an expensive operation
A lot! ES8 is so powerful, in few lines I created something very difficult to do before without a library
MIT