Skip to content

Commit

Permalink
add support for AbortSignal's to runPromise (#2285)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart authored Mar 13, 2024
1 parent 802674b commit 817a04c
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 19 deletions.
18 changes: 18 additions & 0 deletions .changeset/five-games-sneeze.md
Original file line number Diff line number Diff line change
@@ -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);
```
11 changes: 8 additions & 3 deletions packages/effect/src/Effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4672,7 +4672,10 @@ export const runCallback: <A, E>(
* @since 2.0.0
* @category execution
*/
export const runPromise: <A, E>(effect: Effect<A, E>) => Promise<A> = _runtime.unsafeRunPromiseEffect
export const runPromise: <A, E>(
effect: Effect<A, E, never>,
options?: { readonly signal?: AbortSignal } | undefined
) => Promise<A> = _runtime.unsafeRunPromiseEffect

/**
* Runs an `Effect` workflow, returning a `Promise` which resolves with the
Expand All @@ -4681,8 +4684,10 @@ export const runPromise: <A, E>(effect: Effect<A, E>) => Promise<A> = _runtime.u
* @since 2.0.0
* @category execution
*/
export const runPromiseExit: <A, E>(effect: Effect<A, E>) => Promise<Exit.Exit<A, E>> =
_runtime.unsafeRunPromiseExitEffect
export const runPromiseExit: <A, E>(
effect: Effect<A, E, never>,
options?: { readonly signal?: AbortSignal } | undefined
) => Promise<Exit.Exit<A, E>> = _runtime.unsafeRunPromiseExitEffect

/**
* @since 2.0.0
Expand Down
9 changes: 7 additions & 2 deletions packages/effect/src/Runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ export const runCallback: <R>(
* @since 2.0.0
* @category execution
*/
export const runPromise: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>) => Promise<A> =
export const runPromise: <R>(
runtime: Runtime<R>
) => <A, E>(effect: Effect.Effect<A, E, R>, options?: { readonly signal?: AbortSignal } | undefined) => Promise<A> =
internal.unsafeRunPromise

/**
Expand All @@ -149,7 +151,10 @@ export const runPromise: <R>(runtime: Runtime<R>) => <A, E>(effect: Effect.Effec
*/
export const runPromiseExit: <R>(
runtime: Runtime<R>
) => <A, E>(effect: Effect.Effect<A, E, R>) => Promise<Exit.Exit<A, E>> = internal.unsafeRunPromiseExit
) => <A, E>(
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal } | undefined
) => Promise<Exit.Exit<A, E>> = internal.unsafeRunPromiseExit

/**
* @since 2.0.0
Expand Down
41 changes: 28 additions & 13 deletions packages/effect/src/internal/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,30 +257,45 @@ export const unsafeRunSyncExit =
}

/** @internal */
export const unsafeRunPromise =
<R>(runtime: Runtime.Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>): Promise<A> =>
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 = <R>(runtime: Runtime.Runtime<R>) =>
<A, E>(effect: Effect.Effect<A, E, R>, options?: {
readonly signal?: AbortSignal
}): Promise<A> =>
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 =
<R>(runtime: Runtime.Runtime<R>) => <A, E>(effect: Effect.Effect<A, E, R>): Promise<Exit.Exit<A, E>> =>
<R>(runtime: Runtime.Runtime<R>) =>
<A, E>(effect: Effect.Effect<A, E, R>, options?: {
readonly signal?: AbortSignal
}): Promise<Exit.Exit<A, E>> =>
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 */
Expand Down
15 changes: 14 additions & 1 deletion packages/effect/test/Runtime.test.ts
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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 })
)
)
})
})

0 comments on commit 817a04c

Please sign in to comment.