Skip to content

Instantly share code, notes, and snippets.

@lorisleiva
Last active February 23, 2023 17:28
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lorisleiva/d0656b832f95c3d88ec2cf4c93e4a243 to your computer and use it in GitHub Desktop.
Save lorisleiva/d0656b832f95c3d88ec2cf4c93e4a243 to your computer and use it in GitHub Desktop.
Laravel-like pipelines in JavaScript and TypeScript
export function pipeline(initialValue, pipes, then) {
then = then ?? ((t) => t);
const pipelineCallback = pipes
.slice()
.reverse()
.reduce((next, pipe) => (passable) => pipe(passable, next), then);
return pipelineCallback(initialValue);
}
export async function asyncPipeline(initialValue, pipes, then) {
then = then ?? (async (t) => t);
const pipelineCallback = pipes
.slice()
.reverse()
.reduce((next, pipe) => (async (passable) => pipe(await passable, next)), then);
return pipelineCallback(initialValue);
}
const pipeA = (x, next) => next(x + 1);
const pipeB = (x, next) => next(x * 2);
const result = pipeline(10, [pipeA, pipeB]);
console.log(result);
const asyncPipeA = async (x, next) => next(x + 1);
const asyncPipeB = async (x, next) => next(x * 2);
const asyncResult = asyncPipeline(10, [asyncPipeA, asyncPipeB]);
asyncResult.then((x) => { console.log(x); });
type Identity<T> = (t: T) => T
export type Pipe<T> = (t: T, next: Identity<T>) => T;
export function pipeline<T>(initialValue: T, pipes: Pipe<T>[], then: Identity<T> | null = null): T {
then = then ?? ((t) => t);
const pipelineCallback = pipes
.slice()
.reverse()
.reduce<Identity<T>>((next, pipe) => (passable: T) => pipe(passable, next), then);
return pipelineCallback(initialValue);
}
type AsyncIdentity<T> = (t: T) => Promise<T>
export type AsyncPipe<T> = (t: T, next: AsyncIdentity<T>) => Promise<T>;
export async function asyncPipeline<T>(initialValue: T, pipes: AsyncPipe<T>[], then: AsyncIdentity<T> | null = null): Promise<T> {
then = then ?? (async (t) => t);
const pipelineCallback = pipes
.slice()
.reverse()
.reduce<AsyncIdentity<T>>((next, pipe) => (async (passable: T) => pipe(await passable, next)), then);
return pipelineCallback(initialValue);
}
const pipeA: Pipe<number> = (x, next) => next(x + 1);
const pipeB: Pipe<number> = (x, next) => next(x * 2);
const result = pipeline<number>(10, [pipeA, pipeB]);
console.log(result);
const asyncPipeA: AsyncPipe<number> = async (x, next) => next(x + 1);
const asyncPipeB: AsyncPipe<number> = async (x, next) => next(x * 2);
const asyncResult = asyncPipeline<number>(10, [asyncPipeA, asyncPipeB]);
asyncResult.then((x) => { console.log(x); });
@tjventurini
Copy link

tjventurini commented Jun 20, 2022

Nice! πŸ‘

Does this actually work? 🀩

@lorisleiva
Copy link
Author

@tjventurini Yes πŸ™‚ I've used this pattern in a few small apps and really liked it. Not sure how this scale to larger apps though but no reason it shouldn't work.

@tjventurini
Copy link

@lorisleiva Awesome! Thanks for the feedback πŸ‘ Hmm... need to build something with that now πŸ€“

@lorisleiva
Copy link
Author

@tjventurini Do it! πŸš€ 😊

@jaketoolson
Copy link

Thank you! Have been frustrated not having something like this haha. Was spoiled by Laravel's simple pipeline many years ago and here I am, finding this ! You should create a small package about this that can be scaled by community

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment