carbonteq /FP

Async Handling

Mix sync + async validation and effects with Result pipelines.

When to use async pipelines

Use async pipelines when a step needs IO (API calls, DB checks, file reads) but you still want the same short-circuiting behavior as sync Result chains.

The key rule: call .toPromise() at the end of any chain that contains async operations.

Wrapping Async Code

Safe fetch with tryAsyncCatch

Wrap external APIs that might throw:

import {  } from "@carbonteq/fp";

type  = { : string; : string };

async function (: string): <<, Error>> {
  return .(
    async () => {
      const  = await (`/api/users/${}`);
      if (!.) throw new (`HTTP ${.}`);
      return .() as <>;
    },
    () => ( instanceof  ?  : new (())),
  ).();
}

const  = await ("123");
.(); // Result.Ok({ id, name }) or Result.Err(Error)

Chain async operations with flatMap

import {  } from "@carbonteq/fp";

type  = { : string; : number };
type  = { : string };

declare function (: string): <>;
declare function (: ): <>;

async function (: string): <<string, Error>> {
  return .(() => (), () =>  as Error)
    .((: ) =>
      .(() => (), () =>  as Error),
    )
    .((: ) => .)
    .();
}

Mixed Sync + Async Validation

The .validate() operator handles both sync and async validators:

import {  } from "@carbonteq/fp";

type  = { : string; : string };

const  = (: ) =>
  ..("@") ? .() : .("Invalid email");

const  = (: ) =>
  .. >= 8 ? .() : .("Password too short");

declare function (: string): <boolean>;

async function (: ): <<, string>> {
  const  = await (.);
  return  ? .("Email taken") : .();
}

async function (: ) {
  return .()
    .([, , ])
    .();
}

Async Side Effects

Use tap and tapErr for logging or fire-and-forget operations:

import {  } from "@carbonteq/fp";

type  = { : string; : string };

declare function (: ): <, Error>;
declare function (: ): <<, Error>>;
declare function (: string, : string): void;

async function (: ): <<, Error>> {
  return .()
    .()
    .((: ) => .(`Validated: ${.}`))
    .()
    .((: ) => (., `Order ${.} confirmed`))
    .((: Error) => .(`Failed: ${.}`))
    .();
}

Fallback Chains

Use orElse to try alternatives when the primary source fails:

import {  } from "@carbonteq/fp";

type  = { : string; : string };

declare function (: string): <, Error>;
declare function (: string): <, Error>;

function ():  {
  return { : "guest", : "guest-token" };
}

function (: string): <, Error> {
  return ()
    .((: Error) => .(`Redis miss: ${.}`))
    .(() => ())
    .((: Error) => .(`DB miss: ${.}`))
    .(() => .(()));
}

Parallel Async Operations

All must succeed with Result.all

import {  } from "@carbonteq/fp";

type  = { : string };
type  = { : string };

declare function (: string): <<, Error>>;
declare function (: string): <<, Error>>;

async function (: string) {
  const [, ] = await .([
    (),
    (),
  ]);

  return .(, ).(([, ]) => ({
    : .,
    : .,
  }));
}

First success with Result.any

import {  } from "@carbonteq/fp";

type  = { : string };

declare function (): <, Error>;
declare function (): <, Error>;
declare function (): <, Error>;

function (): <, Error[]> {
  return .((), (), ());
}

Tips

PatternWhen to use
tryAsyncCatchWrap code that throws exceptions
validate([...])Multiple guards (sync or async) with error accumulation
flatMap(asyncFn)Chain async operations that return Result
tap / tapErrSide effects (logging, metrics, emails)
orElseFallback chains (cache → DB → default)
Result.allParallel ops where all must succeed
Result.anyParallel ops where first success wins
toPromise()Always call at the end of async chains

On this page