Reduce iterator and pipe

I’m just playing with an idea.

I was working with a pipe sequence, and trying to think how I might do an early exit from the sequence, say if an empty or false value was returned mid sequence.

One idea was to write a custom reduce function that returned early on null or similar, maybe even take a comparator function to achieve this.

What I ended up playing with was reduce as a generator function that returns an iterator. This could then be used with a function like pipe and the sequence looped through with a for…of loop.

Here are the functions

// reduce generator returns an iterator
// can be used with for..of loop, ...spread etc
const _reduceIter = function* (fn, accumulator, arr) {
  it = arr.entries();

  if (accumulator === null) {
    accumulator =[1];

  yield accumulator; // yield first value

  for (const [i, value] of it) {
    accumulator = fn(accumulator, value, i);
    yield accumulator;

// pipe with iterator
const pipeIter = (fn, ...fns) => (...args) =>
  _reduceIter((f, g) => g(f), fn(...args), fns);

I have been trying to think of whether this is useful or not? Thinking of use cases.

Here is a version with an async version of ‘pipeIter’

// async pipe with iterator
const pipeIterAsync = (fn, ...fns) => (...args) =>
  _reduceIter(async (f, g) => g(await f), fn(...args), fns);

// quick and dirty Title Formatter
const toTitle = (str) => str
        (_, b, c) => (b || c).toUpperCase()

// functions for pipe sequence
// 1. fetch JSON
const fetchJson = async (url) => {
  const response = await fetch(url);
  if (response.ok) {
    return response.json();

// 2. Check if object has keys. Return falsy if empty or the object
const hasKeys = (obj) => Object.keys(obj).length && obj;

// 3. Check if 'completed' property is true. Return false or the object
const completed = (obj) => obj?.completed && obj;

// 4. Capitalise title property in object. Return a modified object
const capitaliseTitle = (obj) => ({ ...obj, title: toTitle(obj.title) });

async function getCompletedTask(num) {
  // sequence left to right
  const completedSeq = pipeIterAsync(fetchJson, hasKeys, completed, capitaliseTitle);
  const url = `${num}`
  let obj;
  // iterate through the pipe sequence
  for await (obj of completedSeq(url)) {
    // if a falsy value is returned
    // exit the sequence returning false
    if (!obj) {
      return false;
  return obj;

A quick test

// using .then just to order these asynchronous tests
  .then(() => getCompletedTask(3))
  .then(() => getCompletedTask(199))

Output of the quick tests

// getCompletedTask(4)
  "userId": 1,
  "id": 4,
  "title": "Et Porro Tempora",
  "completed": true

// getCompletedTask(3) not completed

// getCompletedTask(199)
  "userId": 10,
  "id": 199,
  "title": "Numquam Repellendus a Magnam",
  "completed": true

Here is a codepen

This was just a case of playing around with an idea. A bit of a doodle as it were. Just wondering if it has any legs?