Implementing Pipe in JS
Implementing Pipe in JavaScript: Sync and Async Made Easy
Hello, beautiful people on the internet.
The pipe operator (often seen in functional programming) enables you to process data through a sequence of functions, each transforming the output of the previous one.
While JavaScript doesn’t have a native pipe operator yet, we can implement it ourselves for both synchronous and asynchronous use cases.
In this blog, we’ll explore the implementation of a pipe function for synchronous and asynchronous workflows. Let’s dive in!
The Pipe Operator: What and Why?
The pipe operator is a functional programming concept that allows you to chain multiple functions together. It’s like a pipeline where data flows through each function sequentially. Instead of writing deeply nested function calls, you can write cleaner and more readable code.
Example Use Case
Imagine you have two functions:
reverseSentence: Reverses the words in a sentence.capitalizeWords: Capitalizes the first letter of each word.
Here’s the desired transformation:
console.log(capitalizeWords(reverseSentence('My name is Deep')));
// Output: "Deep Is Name My"While this works, it can get messy with more transformations. This is where the pipe operator shines.
Synchronous Pipe Implementation
Let’s start by creating a pipeSync function to handle synchronous transformations. It takes a sequence of functions as arguments and returns a function that applies these transformations in order.
Implementation (normal)
const pipeSync = (...funcs) => {
// The returned function takes an argument `args`, which will be passed through the pipeline of functions.
return args => {
// Initialize `result` with the initial value of `args`.
let result = args;
// Loop through each function in the `funcs` array and apply it to the current `result`.
for (let func of funcs) {
result = func(result); // Update `result` with the output of the current function.
}
// Return the final result after all functions have been applied.
return result;
};
};Implementation (using reduce)
const pipeSync = (...funcs) => {
// The returned function takes `args` as the initial value to start the pipeline.
return args => {
// Use `reduce` to apply each function in `funcs` to the accumulated result (`acc`), starting with the initial `args`.
return funcs.reduce((acc, func) => {
// Apply the current function `func` to the accumulated result `acc` and return it for the next iteration.
return func(acc);
}, args); // `args` is the initial value passed into the pipeline.
};
};How It Works
pipeSyncaccepts any number of functions as arguments.- It returns a new function that applies these functions to the input in sequence.
- Each function’s output becomes the input for the next function.
Example Usage
const reverseSentence = sentence => sentence.split(' ').reverse().join(' ');
const capitalizeWords = sentence => sentence.split(' ').map(word => `${word[0].toUpperCase()}${word.slice(1)}`).join(' ');
const pipeSyncExample = pipeSync(
reverseSentence,
capitalizeWords
);
console.log(pipeSyncExample('My name is Deep'));
// Output: "Deep Is Name My"Asynchronous Pipe Implementation
Working with promises and asynchronous functions? Let’s extend the pipe concept to handle them. Enter pipeAsync!
Implementation (normal)
const pipeAsync = (...funcs) => {
// The returned async function takes `args`, which can be a promise or a value.
return async args => {
// Wait for the initial `args` to resolve (if it's a promise).
let result = await args;
// Loop through each function in the `funcs` array, awaiting each function's result.
for (let func of funcs) {
// Apply the current function `func` to the result and wait for its resolved value.
result = await func(result);
}
// Return the final result after all asynchronous functions have been applied.
return result;
};
};Implementation (using reduce)
const pipeAsync = (...funcs) => {
// The returned function takes `args`, which is the initial value that will be passed through the pipeline.
return args => {
// Use `reduce` to chain the asynchronous functions together.
return funcs.reduce((acc, func) => {
// For each function, the accumulator `acc` is a Promise, and we resolve it using `then`.
// Each function `func` is applied to the resolved value of `acc`.
return acc.then(result => func(result));
}, Promise.resolve(args)); // Start the chain by wrapping the initial `args` in a resolved Promise.
};
};How It Works
pipeAsyncis similar topipeSync, but it handles asynchronous functions byawaiting their results.- The input can also be a promise, making it flexible for real-world scenarios.
Example Usage
Let’s say we have two asynchronous functions:
const reverseSentenceAsync = sentence => {
return new Promise(resolve => {
console.log("Reversing:", sentence);
setTimeout(() => {
resolve(sentence.split(' ').reverse().join(' '));
}, 3000);
});
};
const capitalizeWordsAsync = sentence => {
return new Promise(resolve => {
console.log("Capitalizing:", sentence);
setTimeout(() => {
resolve(sentence.split(' ').map(word => `${word[0].toUpperCase()}${word.slice(1)}`).join(' '));
}, 3000);
});
};
const pipeAsyncExample = pipeAsync(
reverseSentenceAsync,
capitalizeWordsAsync
);
pipeAsyncExample('My name is Deep').then(result => {
console.log("Final Result:", result);
});Output
Reversing: My name is Deep
Capitalizing: Deep is name My
Final Result: Deep Is Name MyReal-World Use Cases of Pipe Operators
1. Data Transformation
In data pipelines, you often need to perform multiple transformations on raw data. The pipe operator makes these transformations clean and composable.
2. Middleware Chaining
In web frameworks like Express, middleware functions can be thought of as a pipeline that processes requests sequentially.
3. Functional Composition
The pipe operator embodies the essence of functional programming: composing small, reusable functions to solve complex problems.
Conclusion
The pipe operator is a powerful tool for writing clean, readable, and maintainable code. Whether working with synchronous or asynchronous workflows, implementing your pipe function in JavaScript can simplify complex function chains and improve code clarity.
Get Deep Shah’s stories in your inbox
Join Medium for free to get updates from this writer.
Try using pipeSync and pipeAsync in your projects and see how they transform your coding experience!
I’m always looking to improve and would love to hear your thoughts! If you have any suggestions, feedback, or topics you’d like me to cover, please feel free to share them in the comments below. Your insights are greatly appreciated! 🙂
Thank you for being a part of the community
Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: X | LinkedIn | YouTube | Newsletter | Podcast
- Check out CoFeed, the smart way to stay up-to-date with the latest in tech 🧪
- Start your own free AI-powered blog on Differ 🚀
- Join our content creators community on Discord 🧑🏻💻
- For more content, visit plainenglish.io + stackademic.com









