Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

composeAsync

Composes asynchronous functions from right to left.

TypeScript
Copied!
1/**
2 * Composes asynchronous functions from right to left.
3 *
4 * @param fns - Functions to compose (right to left).
5 * @returns A function that applies the composed functions to an input.
6 */
7export function composeAsync<T>(
8  ...fns: Array<(input: any) => Promise<any> | any>
9): (input: T) => Promise<any> {
10  return async (input: T): Promise<any> => {
11    let result = input;
12    for (let i = fns.length - 1; i >= 0; i--) {
13      result = await fns[i](result);
14    } 
15    return result;
16  };
17}
  • Supports Mixed Async and Sync Functions

    Composes any combination of asynchronous and synchronous functions without extra boilerplate.

  • Right-to-Left Execution Order

    Aligns with traditional composition notation (f(g(x))), making it familiar to functional programming users.

  • Robust Promise Handling

    Internally uses await to handle any type of function return (sync or Promise), ensuring consistent behavior.

  • Readable and Maintainable

    Clear loop-based implementation improves debuggability compared to nested promises or recursive calls.

Tests | Examples

TypeScript
Copied!
1test('composeAsync - composes two async functions', async () => {
2  const add = async (x: number) => x + 2;
3  const double = async (x: number) => x * 2;
4
5  const composed = composeAsync(add, double);
6  const result = await composed(3);
7
8  expect(result).toBe(8); // double(3) = 6; add(6) = 8
9});
10
11test('composeAsync - handles mixed sync and async functions', async () => {
12  const toStr = (x: number) => `Value: ${x}`;
13  const increment = async (x: number) => x + 1;
14
15  const composed = composeAsync(toStr, increment);
16  const result = await composed(4);
17
18  expect(result).toBe('Value: 5');
19});
20
21test('composeAsync - returns original value when no functions', async () => {
22  const composed = composeAsync();
23  const result = await composed('test');
24
25  expect(result).toBe('test');
26});

Common Use Cases

  • Async Middleware Composition

    Compose multiple middlewares or interceptors in backend frameworks or API handlers.

  • Data Transformation Pipelines

    Build right-to-left pipelines for transforming fetched data, applying validators, or formatting results.

  • Async Validation Chains

    Combine multiple async validation functions for processing forms, payloads, or external input.

  • Workflow Engines or Task Runners

    Dynamically compose async steps for executing multi-stage jobs or ETL pipelines.

  • Functional Reactive Patterns

    Apply composed transformations to observable or event stream data that resolves asynchronously.

Codebase: Utilities -> Functions -> composeAsync | Yevhen Klymentiev