diff --git a/.changeset/five-games-sneeze.md b/.changeset/five-games-sneeze.md
new file mode 100644
index 0000000000..31f2bb2c8b
--- /dev/null
+++ b/.changeset/five-games-sneeze.md
@@ -0,0 +1,18 @@
+---
+"effect": patch
+---
+
+add support for AbortSignal's to runPromise
+
+If the signal is aborted, the effect execution will be interrupted.
+
+```ts
+import { Effect } from "effect";
+
+const controller = new AbortController();
+
+Effect.runPromise(Effect.never, { signal: controller.signal });
+
+// abort after 1 second
+setTimeout(() => controller.abort(), 1000);
+```
diff --git a/packages/effect/src/Effect.ts b/packages/effect/src/Effect.ts
index c0bd6a300b..6478554db3 100644
--- a/packages/effect/src/Effect.ts
+++ b/packages/effect/src/Effect.ts
@@ -4672,7 +4672,10 @@ export const runCallback: (
* @since 2.0.0
* @category execution
*/
-export const runPromise: (effect: Effect) => Promise = _runtime.unsafeRunPromiseEffect
+export const runPromise: (
+ effect: Effect,
+ options?: { readonly signal?: AbortSignal } | undefined
+) => Promise = _runtime.unsafeRunPromiseEffect
/**
* Runs an `Effect` workflow, returning a `Promise` which resolves with the
@@ -4681,8 +4684,10 @@ export const runPromise: (effect: Effect) => Promise = _runtime.u
* @since 2.0.0
* @category execution
*/
-export const runPromiseExit: (effect: Effect) => Promise> =
- _runtime.unsafeRunPromiseExitEffect
+export const runPromiseExit: (
+ effect: Effect,
+ options?: { readonly signal?: AbortSignal } | undefined
+) => Promise> = _runtime.unsafeRunPromiseExitEffect
/**
* @since 2.0.0
diff --git a/packages/effect/src/Runtime.ts b/packages/effect/src/Runtime.ts
index cf135d57d3..d6103c25bb 100644
--- a/packages/effect/src/Runtime.ts
+++ b/packages/effect/src/Runtime.ts
@@ -134,7 +134,9 @@ export const runCallback: (
* @since 2.0.0
* @category execution
*/
-export const runPromise: (runtime: Runtime) => (effect: Effect.Effect) => Promise =
+export const runPromise: (
+ runtime: Runtime
+) => (effect: Effect.Effect, options?: { readonly signal?: AbortSignal } | undefined) => Promise =
internal.unsafeRunPromise
/**
@@ -149,7 +151,10 @@ export const runPromise: (runtime: Runtime) => (effect: Effect.Effec
*/
export const runPromiseExit: (
runtime: Runtime
-) => (effect: Effect.Effect) => Promise> = internal.unsafeRunPromiseExit
+) => (
+ effect: Effect.Effect,
+ options?: { readonly signal?: AbortSignal } | undefined
+) => Promise> = internal.unsafeRunPromiseExit
/**
* @since 2.0.0
diff --git a/packages/effect/src/internal/runtime.ts b/packages/effect/src/internal/runtime.ts
index 49751dc33e..b36cbf7949 100644
--- a/packages/effect/src/internal/runtime.ts
+++ b/packages/effect/src/internal/runtime.ts
@@ -257,30 +257,45 @@ export const unsafeRunSyncExit =
}
/** @internal */
-export const unsafeRunPromise =
- (runtime: Runtime.Runtime) => (effect: Effect.Effect): Promise =>
- unsafeRunPromiseExit(runtime)(effect).then((result) => {
- switch (result._tag) {
- case OpCodes.OP_SUCCESS: {
- return result.i0
- }
- case OpCodes.OP_FAILURE: {
- throw fiberFailure(result.i0)
- }
+export const unsafeRunPromise = (runtime: Runtime.Runtime) =>
+(effect: Effect.Effect, options?: {
+ readonly signal?: AbortSignal
+}): Promise =>
+ unsafeRunPromiseExit(runtime)(effect, options).then((result) => {
+ switch (result._tag) {
+ case OpCodes.OP_SUCCESS: {
+ return result.i0
}
- })
+ case OpCodes.OP_FAILURE: {
+ throw fiberFailure(result.i0)
+ }
+ }
+ })
/** @internal */
export const unsafeRunPromiseExit =
- (runtime: Runtime.Runtime) => (effect: Effect.Effect): Promise> =>
+ (runtime: Runtime.Runtime) =>
+ (effect: Effect.Effect, options?: {
+ readonly signal?: AbortSignal
+ }): Promise> =>
new Promise((resolve) => {
const op = fastPath(effect)
if (op) {
resolve(op)
}
- unsafeFork(runtime)(effect).addObserver((exit) => {
+ const fiber = unsafeFork(runtime)(effect)
+ fiber.addObserver((exit) => {
resolve(exit)
})
+ if (options?.signal !== undefined) {
+ if (options.signal.aborted) {
+ fiber.unsafeInterruptAsFork(fiber.id())
+ } else {
+ options.signal.addEventListener("abort", () => {
+ fiber.unsafeInterruptAsFork(fiber.id())
+ })
+ }
+ }
})
/** @internal */
diff --git a/packages/effect/test/Runtime.test.ts b/packages/effect/test/Runtime.test.ts
index 1ab8d76e14..2ca0c6bc2b 100644
--- a/packages/effect/test/Runtime.test.ts
+++ b/packages/effect/test/Runtime.test.ts
@@ -1,4 +1,4 @@
-import { Effect, FiberRef, Layer, Runtime } from "effect"
+import { Effect, Exit, FiberRef, Layer, Runtime } from "effect"
import { assert, describe } from "vitest"
import * as it from "./utils/extend.js"
@@ -27,4 +27,17 @@ describe("Runtime", () => {
result = Runtime.runSync(Runtime.deleteFiberRef(runtime, ref))(FiberRef.get(ref))
assert.deepStrictEqual(result, { value: 0 })
}))
+
+ it.it("runPromiseExit/signal", async () => {
+ const aborted = AbortSignal.abort()
+ assert(Exit.isInterrupted(await Runtime.runPromiseExit(Runtime.defaultRuntime)(Effect.never, { signal: aborted })))
+
+ const controller = new AbortController()
+ setTimeout(() => controller.abort(), 10)
+ assert(
+ Exit.isInterrupted(
+ await Runtime.runPromiseExit(Runtime.defaultRuntime)(Effect.never, { signal: controller.signal })
+ )
+ )
+ })
})