AniUI Academy

Pipe async steps in sequence

hardFunctions & Closures+350 XPPractice

A request pipeline is rarely all synchronous: parse the input, fetch something, validate the response, map it to view state. Some steps return promises and some do not, and they must run in order because each depends on the last — which rules out Promise.all and rules out forEach.

Implement pipeAsync(...fns). It returns an async function that calls the leftmost function with all of the arguments it received, awaits the result, passes that value to the next function, and so on left to right, resolving with the last function's value. Steps run strictly in sequence: a step is not started until the previous one has settled. Steps may be synchronous or asynchronous and are treated identically. If any step throws or returns a rejected promise, the returned promise rejects with that error and no later step is called. With no functions at all, the returned function resolves with its first argument unchanged.

Arguments

...fnsFunction[]
Any mix of sync and async unary functions. The leftmost may take any arity.

Returns

(...args) => Promise<any> — An async function that threads a value through fns left to right, awaiting each step.

Examples

const run = pipeAsync(
  (n) => n + 1,                                        // sync
  async (n) => { await delay(5); return n * 2; },     // async
  (n) => "value:" + n                                  // sync
);

await run(4); // "value:10"

Sync and async steps mix freely — await on a non-promise is a no-op.

try {
  await pipeAsync(
    async () => { throw new Error("stop"); },
    () => "never reached"
  )(1);
} catch (e) {
  // e.message === "stop" — later steps skipped on the first error
}

What it has to do

  • Thread the value left to right, awaiting each step before starting the next.
  • Treat synchronous and promise-returning steps identically.
  • Pass every argument to the leftmost step.
  • Reject with the first error, whether thrown synchronously or rejected, and skip the remaining steps.
  • With no functions, resolve with the first argument unchanged.

Notes

  • A plain for loop with await inside an async function gives you sequencing for free. Promise.all would run steps in parallel — wrong here.
  • await on a non-promise just resolves to the value, so you never need to detect whether a step returned a promise.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.