Skip to content

Instantly share code, notes, and snippets.

@barelyhuman
Last active November 12, 2023 23:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barelyhuman/729b7da7cd7bb1a7781ad366031205bc to your computer and use it in GitHub Desktop.
Save barelyhuman/729b7da7cd7bb1a7781ad366031205bc to your computer and use it in GitHub Desktop.
pipe

Ever changing implementations of pipes that I reimplement in each codebase for different use cases.

If you do want a package instead. You should go with this https://github.com/barelyhuman/pipe

/**
*
* @param {...function} args
* @returns
* @example
* console.log(
* await pipe(
* () => [1, 2, 3],
* () => pipe(() => prev.map((x) => x * 2)),
* (counts) => counts.map(async (x) => x * 2)
* )
* );
*/
function pipe(...args) {
return args.reduce((acc, item, index) => {
if (typeof item !== "function")
throw new Error(`[pipe] item at index: ${index} is not a function`);
return acc.then((prev) => {
const res = item(prev);
return Array.isArray(res) && res.every((x) => x instanceof Promise)
? Promise.all(res)
: res;
});
}, Promise.resolve());
}
const pipe = (initData, chain = []) => {
let _data = initData;
if (typeof initData === "function") {
_data = initData();
}
return {
_(fn) {
chain.push(fn);
return this;
},
async value() {
let initValue = _data;
if (_data instanceof Promise) {
initValue = await _data;
}
return chain.reduce((acc, item) => {
return acc.then((prev) => {
return item(prev);
});
}, Promise.resolve(initValue));
},
};
};
const isArrayOfPromises = (item) =>
Boolean(Array.isArray(item) && item.every((x) => x instanceof Promise));
const resolveArray = (item) =>
(isArrayOfPromises(item) && Promise.all(item)) || item;
/**
* @description handles both sync and async cases. If even on function in the
* pipe params is async or returns an array of promises, the entire pipe is considered
* to be async.
* if not then it runs the functions in sync
* @param {...function} args
* @returns
* @example
* // gives the result as sync, instead of a promise.
* const nextHour = pipe(
* () => new Date(),
* (d) => d.setHours(d.getHours() + 1),
* (x) => new Date(x)
* )
*
* // gives the result as async, and needs to be awaited
* const nextHour = await pipe(
* () => new Date(),
* async (d) => d.setHours(d.getHours() + 1),
* (x) => new Date(x)
* )
*/
export const pipe = (...args) => {
let syncFlow = true,
result = undefined;
for (let i = 0; i < args.length; i += 1) {
const fn = args[i];
if (!syncFlow) {
result = Promise.resolve(result)
.then(resolveArray)
.then((d) => fn(d));
} else {
result = fn(result);
if (result instanceof Promise || isArrayOfPromises(result)) {
syncFlow = false;
}
}
}
if (result && typeof result.then === "function") {
return result.then(resolveArray);
}
return result;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment