Skip to content

Feature request: flowAsync #1658

Closed

Description

@zrosenbauer

Problem

flow silently breaks when any composed function returns a Promise. It passes the raw Promise object to the next function instead of the resolved value:

const fetchUser = async (id: number) => db.users.find(id);
const getName = (user: User) => user.name;

const getUserName = flow(fetchUser, getName);
await getUserName(1); // TypeError: Cannot read property 'name' of [object Promise]

The implementation has no await:

// current flow implementation
for (let i = 1; i < funcs.length; i++) {
  result = funcs[i].call(this, result); // passes raw Promise
}

Proposal

const getUserName = flowAsync(fetchUser, getName);
await getUserName(1); // "Alice"

Implementation is flow + async + two awaits:

export function flowAsync(...funcs) {
  return async function (this: any, ...args: any[]) {
    let result = funcs.length ? await funcs[0].apply(this, args) : args[0];

    for (let i = 1; i < funcs.length; i++) {
      result = await funcs[i].call(this, result);
    }

    return result;
  };
}

Precedent

es-toolkit already follows a sync/Async naming convention:

  • attempt / attemptAsync
  • filter / filterAsync
  • flatMap / flatMapAsync
  • map / mapAsync
  • reduce / reduceAsync

flow / flowAsync fits this pattern exactly.

The TC39 proposal-function-pipe-flow included Function.flowAsync but was withdrawn at Stage 1, with the committee noting these are "easily solved by userland functions" — which is what es-toolkit provides.

Activity

  1. zrosenbauer commented on Mar 19, 2026

    @zrosenbauer
    Author

    FYI I'm happy to open pull for this but don't want to do the work unless core team approves.

  2. added a commit that references this issue on Jul 20, 2026
    9cac0a8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions