Effect.runPromise

We’ve seen how Effects can be used to model programs. We’ve also seen how constructing an Effect is separate from executing that Effect. Over the next few lessons we’ll see how to construct and execute asynchronous Effects.

Let’s take a look at how to execute an Effect asynchronously.

import { Effect } from "effect";

const program = Effect.succeed(3);

// previously:
const syncResult: number = Effect.runSync(program);

// new:
const asyncResult: Promise<number> = Effect.runPromise(program);

asyncResult
	.then((x: number) => console.log("program succeeded with", x))
	.catch((e: unknown) => console.error("program failed with", e));

Note that any Effect can be run asynchronously, regardless of how it was created.

As you might expect, an Effect that fails will throw when run with Effect.runPromise.

import { Effect } from "effect";

const badProgram = Effect.fail(new Error("uh oh"));

Effect.runPromise(badProgram)
	.then((x: never) => console.log("program succeeded with", x))
	.catch((e: unknown) => console.error("program failed with", e));

As with Effect.runSync, we lose any type safety on the error branch. This is why it is usually preferable to move error handling inside the Effectful program.

import { Effect } from "effect";

const badProgram = Effect.fail(new Error("uh oh"));

badProgram.pipe(
	Effect.map((x) => console.log("program succeeded with", x)),
	Effect.catchAll((e: Error) =>
		Effect.succeed(console.error("program failed with", e.message)),
	),
	Effect.runPromise
);

You can even write a wrapper that only runs Effects that have no tracked failures.

import { Effect } from "effect";

const runPromiseSafely = <A>(effect: Effect.Effect<A, never>) => Effect.runPromise(effect)

const badProgram = Effect.fail(new Error("uh oh"));

badProgram.pipe(
	Effect.map((x) => console.log("program succeeded with", x)),
	Effect.catchAll((e: Error) =>
		Effect.succeed(console.error("program failed with", e.message)),
	),
	runPromiseSafely
);

// tsserver says Argument of type 'Effect<never, Error, never>' is not assignable to parameter of type 'Effect<never, never, never>'. Type 'Error' is not assignable to type 'never'.
runPromiseSafely(badProgram);

Let’s recap.

  • Effect.runPromise allows you to run an Effect asynchronously
  • Effect.runPromise returns a Promise
  • The promise will resolve to a value if the Effect was successful
  • The promise will reject if the Effect failed
  • Synchronous effects can be run asynchronously