All Projects → egeozcan → Ppipe

egeozcan / Ppipe

Licence: isc
pipes values through functions, an alternative to using the proposed pipe operator ( |> ) for ES

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Ppipe

Promised Pipe
A ramda.pipe-like utility that handles promises internally with zero dependencies
Stars: ✭ 64 (-66.67%)
Mutual labels:  async, promise, pipe
Metasync
Asynchronous Programming Library for JavaScript & Node.js
Stars: ✭ 164 (-14.58%)
Mutual labels:  chain, async, promise
Jdeferred
Java Deferred/Promise library similar to JQuery.
Stars: ✭ 1,483 (+672.4%)
Mutual labels:  async, promise
Bach
Compose your async functions with elegance.
Stars: ✭ 117 (-39.06%)
Mutual labels:  async, promise
Rubico
[a]synchronous functional programming
Stars: ✭ 133 (-30.73%)
Mutual labels:  async, promise
Fluture
🦋 Fantasy Land compliant (monadic) alternative to Promises
Stars: ✭ 2,249 (+1071.35%)
Mutual labels:  async, promise
Taskorama
⚙ A Task/Future data type for JavaScript
Stars: ✭ 90 (-53.12%)
Mutual labels:  async, promise
Kitchen Async
A Promise library for ClojureScript, or a poor man's core.async
Stars: ✭ 128 (-33.33%)
Mutual labels:  async, promise
Emittery
Simple and modern async event emitter
Stars: ✭ 1,146 (+496.88%)
Mutual labels:  async, promise
Unityfx.async
Asynchronous operations (promises) for Unity3d.
Stars: ✭ 143 (-25.52%)
Mutual labels:  async, promise
Functional Promises
Write code like a story w/ a powerful Fluent (function chaining) API
Stars: ✭ 141 (-26.56%)
Mutual labels:  async, promise
Tascalate Concurrent
Implementation of blocking (IO-Bound) cancellable java.util.concurrent.CompletionStage and related extensions to java.util.concurrent.ExecutorService-s
Stars: ✭ 144 (-25%)
Mutual labels:  async, promise
Datakernel
Alternative Java platform, built from the ground up - with its own async I/O core and DI. Ultra high-performance, simple and minimalistic - redefines server-side programming, web-development and highload!
Stars: ✭ 87 (-54.69%)
Mutual labels:  async, promise
Pipe
[READONLY] Library for implementing function call chains
Stars: ✭ 70 (-63.54%)
Mutual labels:  chain, pipe
Tedis
redis client with typescript and esnext for nodejs
Stars: ✭ 109 (-43.23%)
Mutual labels:  async, promise
Write
Write data to the file system, creating any intermediate directories if they don't already exist. Used by flat-cache and many others!
Stars: ✭ 68 (-64.58%)
Mutual labels:  async, promise
Tas
Make it easy to develop large, complex Node.js app.
Stars: ✭ 128 (-33.33%)
Mutual labels:  async, promise
Flowa
🔥Service level control flow for Node.js
Stars: ✭ 66 (-65.62%)
Mutual labels:  async, promise
Sieppari
Small, fast, and complete interceptor library for Clojure/Script
Stars: ✭ 133 (-30.73%)
Mutual labels:  async, promise
Async Busboy
Promise based multipart form parser for KoaJS
Stars: ✭ 159 (-17.19%)
Mutual labels:  async, promise

PPIPE

build Coverage Status npm npm license DeepScan Grade

Please note that this library is considered "done". It is still maintained and will be in the foreseeable future, but, other than adding Typescript support, no new functionality will be added. At least, there is no plan to do so. This library has an extensive test suite with 100% coverage, and it is used by at least a few well-established projects in production. The mythical "production-ready" seems to be reached :)

All bug reports and suggestions are still welcome!

pipes values through functions, an alternative to using the proposed pipe operator ( |> ) for ES.

Demo available on RunKit.

Supports functions returning promises too. In that case, the result of the chain will also be a promise. This is similar to the proposed support for await in the chained functions.

Installation

npm install ppipe

Problems ppipe solves

Let's assume you have these functions:

const add = (x, y) => x + y;
const square = x => x * x;
const divide = (x, y) => x / y;
const double = x => x + x;

How do you pass the results from one to another?

//good old single line solution
add(divide(square(double(add(1, 1))), 8), 1);
//try to get creative with variable names?
const incremented = add(1, 1);
const doubled = double(incremented);
//...

An ideal solution would have been having a pipe operator (|>) but we don't have it. Here is where ppipe comes in.

Order of arguments can be manipulated using the _ property of ppipe function. The result of the previous function is inserted to its place if it exists in the arguments. It can also occur more than once if you want to pass the same parameter more than once. If, and only if, _ doesn't exist among the arguments, the piped value will be inserted at the end.

const ppipe = require("ppipe");
const _ = ppipe._;
ppipe(1)(add, 1)(double)(square)(divide, _, 8)(add, 1)(); // 3

If that is too lisp-y, you can also use ".pipe".

ppipe(1)
  .pipe(add, 1)
  .pipe(double)
  .pipe(square)
  .pipe(divide, _, 8)
  .pipe(add, 1)(); // 3

And then you receive some new "requirements", which end up making the "double" function async...

async function asyncDouble(x) {
  const result = x * 2;
  await someAPICall(result);
  return result;
}

Here are the changes you need to make:

await ppipe(1)
  .pipe(add, 1)
  .pipe(asyncDouble)
  .pipe(square)
  .pipe(divide, _, 8)
  .pipe(add, 1); //3 (you can also use .then and .catch)

Yes, ppipe automatically turns the end result into a promise, if one or more functions in the chain return a promise. It also waits for the resolution and passes the unwrapped value to the next function. You can also catch the errors with .catch like a standard promise or use try/catch in an async function. You meet the requirements and keep the code tidy.

For consistency, the .then and .catch methods are always available, so you don't have to care if any function in the chain is async as long as you use those.

So, later you receive some new "requirements", which make our now infamous double function return an object:

async function asyncComplexDouble(x) {
  const result = x * 2;
  const someInfo = await someAPICall(result);
  return { result, someInfo };
}

Still not a problem:

await ppipe(1)
  .pipe(add, 1)
  .pipe(asyncComplexDouble)
  //pipe._ is also a proxy which saves the property accesses to pluck the prop from the
  //previous function's result later
  .pipe(square, _.result)
  .pipe(divide, _, 8)
  .pipe(add, 1); //3

//well, if you think that might not be clear, you can write it like this, too
await ppipe(1)
  .pipe(add, 1)
  .pipe(asyncComplexDouble)
  .pipe(x => x.result)
  .pipe(square)
  .pipe(divide, _, 8)
  .pipe(add, 1); //3

//this also works
await ppipe(1)
  .pipe(add, 1)
  .pipe(asyncComplexDouble)
  //promises will be unboxed and properties will be returned as getter functions
  //the methods will be available in the chain as well, as shown in the next example
  .result()
  .pipe(square)
  .pipe(divide, _, 8)
  .pipe(add, 1); //3

Let's go one step further; what if you need to access a method from the result?

async function advancedDouble(x) {
  const result = x * 2;
  const someInfo = await someAPICall(result);
  return {
    getResult() {
      return result;
    },
    someInfo
  };
}

There you go:

await ppipe(1)
  .pipe(add, 1)
  .pipe(advancedDouble)
  .getResult()
  .pipe(square)
  .pipe(divide, _, 8)
  .pipe(add, 1); //3

Some More Examples

It is possible to expand the iterable result

const addAll = (...x) => x.reduce((a, b) => a + b, 0)
ppipe([1,2,3]).map(x => x + 1).pipe(addAll, ..._)(); //9

It is possible to reach array members:

await ppipe(10)
    .pipe(asyncComplexDoubleArray)
    .pipe((x, y) => x + y, _[1], _[2]);

Also object properties:

ppipe(10)
    .pipe(x => ({multipliers: [10,20], value: x}))
    .pipe((x, y) => x * y, _.multipliers[0], _.value)(); //100

And you can omit the function altogether if you just want to extract values:

ppipe({multipliers: [10,20], value: 10}).pipe(_.value)(); //10
await ppipe({multipliers: [10,20], value: 10}).pipe(_.value); //10

And as you've seen before, you can always omit the ".pipe", as long as you know how to keep ASI in check:

ppipe({multipliers: [10,20], value: 10})(_.value)(); //10
await ppipe({multipliers: [10,20], value: 10})(_.value); //10

Advanced Functionality

Chain Methods / Properties

You can use these from the chain (after creating one with ppipe(val)).

.with(ctx)

Calls the following function in chain with the given this value (ctx). After calling .with the chain can be continued with the methods from the ctx.

class Example {
  constructor(myInt) {
    this.foo = Promise.resolve(myInt);
  }
  addToFoo(x) {
    return this.foo.then(foo => foo + x);
  }
}
await ppipe(10).with(new Example(5)).addToFoo(_); //15

Look at the test/test.js for more examples.

.val

Gets the current value from the chain. Will be a promise if any function in the chain returns a promise. Calling the chain with no parameters achieves the same result.

Extending Ppipe

You can create an extended instance of ppipe via .extend.

const newPipe = ppipe.extend({
  divide (x, y) {
    return x / y;
  },
  log(...params) {
    console.log(...params);
    return params[params.length - 1];
  }
});
const res = await newPipe(10)
  .pipe(x => x + 1)
  .divide(_, 11)
  .log("here is our x: ") //logs "here is our x: 1"
  .pipe(x => x + 1) // 2

You can also call .extend on the extended ppipes. It will create a new ppipe with the new and existing extensions merged.

Testing

All the functionality is tested, with 100% coverage. This is also integrated in the build process.

To run the tests yourself, clone the repository, install the dev dependencies, and run the npm test command.

npm install

npm test

Contributing

See CONTRIBUTING.

Changelog

  • v2.5.0 - placeholder can be the only argument to the .pipe, for just extracting a property or path
  • v2.4.0 - allow deep property extraction via the placeholder (_.deeply.nested.prop) (test: should be able to extract array members)
  • v2.3.0 - now supports expanding the placeholder (..._) (test: should support expanding the array result)

Caveats

  • This library was not written with performance in mind. So, it makes next to no sense to use it in, say, a tight loop. Use in a web-server should be fine as long as you don't have tight response-time requirements. General rule of thumb: Test it before putting it into prod. There are a lot of tests written for ppipe but none of them measure performance. I may improve the performance in the future (some low-hanging fruits) but I'd rather avoid making any guarantees. Well, there is one good news: Chrome team is working on performance improvements to the Proxy which will very positively affect ppipe performance.

  • It uses ES6 Proxies to do its magic. Proxies are not back-portable. 1.x.x versions of ppipe didn't use proxies. So you can try using an older version with a transpiler if evergreen sounds alien to you. Here is an older stable version without value extracting and context change support.

  • ppipe is not typed. No type definition exists for TypeScript nor Flow. I actually love TypeScript and would support it but the lack of variadic generic type parameters make it next to impossible to provide type definitions for ppipe. More can be read here. Also, ppipe is as dynamic as it gets, giving the ability to access virtual properties/methods which may belong to the provided context, the processed value or any of the possible extensions. TypeScripts Type System is Turing Complete, so, maybe there is a way to type all of this but I really need help about that.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].