Skip to main content
This module provides utilities for working with Deferred, a powerful concurrency primitive that represents an asynchronous variable that can be set exactly once. Multiple fibers can await the same Deferred and will all be notified when it completes.

Overview

A Deferred<A, E> is like a promise that can be:
  • Completed successfully with a value of type A
  • Failed with an error of type E
  • Interrupted if the fiber setting it is interrupted
Key characteristics:
  • Single assignment: Can only be completed once
  • Multiple waiters: Many fibers can await the same Deferred
  • Fiber-safe: Thread-safe operations across concurrent fibers
  • Composable: Works seamlessly with other Effect operations

Basic Usage

import { Deferred, Effect, Fiber } from "effect"

// Coordinate between fibers
const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<string, never>()

  // Fiber 1: waits for the value
  const waiter = yield* Effect.forkChild(
    Effect.gen(function*() {
      const value = yield* Deferred.await(deferred)
      console.log("Received:", value)
      return value
    })
  )

  // Fiber 2: sets the value after a delay
  const setter = yield* Effect.forkChild(
    Effect.gen(function*() {
      yield* Effect.sleep("1 second")
      yield* Deferred.succeed(deferred, "Hello from setter!")
    })
  )

  yield* Fiber.join(waiter)
  yield* Fiber.join(setter)
})

Types

Deferred

interface Deferred<in out A, in out E = never>
An asynchronous variable that can be set exactly once. Multiple fibers can suspend waiting for the value.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  // Create a Deferred that will hold a string value
  const deferred: Deferred.Deferred<string> = yield* Deferred.make<string>()

  yield* Deferred.succeed(deferred, "Hello, World!")
  
  const value = yield* Deferred.await(deferred)
  console.log(value) // "Hello, World!"
})

Creating Deferreds

make

const make: <A, E = never>() => Effect<Deferred<A, E>>
Creates a new Deferred.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  yield* Deferred.succeed(deferred, 42)
  
  const value = yield* Deferred.await(deferred)
  console.log(value) // 42
})

makeUnsafe

const makeUnsafe: <A, E = never>() => Deferred<A, E>
Unsafely creates a new Deferred without Effect wrapping.
import { Deferred } from "effect"

const deferred = Deferred.makeUnsafe<number>()
console.log(deferred)

Awaiting Values

await

const await: <A, E>(self: Deferred<A, E>) => Effect<A, E>
Retrieves the value of the Deferred, suspending the fiber until the result is available.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  yield* Deferred.succeed(deferred, 42)

  const value = yield* Deferred.await(deferred)
  console.log(value) // 42
})

Completing Deferreds

succeed

const succeed: {
  <A>(value: A): <E>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, value: A): Effect<boolean>
}
Completes the Deferred with the specified value. Returns false if already completed.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  yield* Deferred.succeed(deferred, 42)

  const value = yield* Deferred.await(deferred)
  console.log(value) // 42
})

sync

const sync: {
  <A>(evaluate: LazyArg<A>): <E>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, evaluate: LazyArg<A>): Effect<boolean>
}
Completes the Deferred with a lazily evaluated value.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  yield* Deferred.sync(deferred, () => 42)

  const value = yield* Deferred.await(deferred)
  console.log(value) // 42
})

complete

const complete: {
  <A, E, R>(effect: Effect<A, E, R>): (self: Deferred<A, E>) => Effect<boolean, never, R>
  <A, E, R>(self: Deferred<A, E>, effect: Effect<A, E, R>): Effect<boolean, never, R>
}
Completes the deferred with the result of an effect. The result is memoized.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  const completed = yield* Deferred.complete(deferred, Effect.succeed(42))
  console.log(completed) // true

  const value = yield* Deferred.await(deferred)
  console.log(value) // 42
})

completeWith

const completeWith: {
  <A, E>(effect: Effect<A, E>): (self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, effect: Effect<A, E>): Effect<boolean>
}
Completes the deferred with an effect (not memoized).
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  const completed = yield* Deferred.completeWith(deferred, Effect.succeed(42))
  console.log(completed) // true

  const value = yield* Deferred.await(deferred)
  console.log(value) // 42
})

Failing Deferreds

fail

const fail: {
  <E>(error: E): <A>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, error: E): Effect<boolean>
}
Fails the Deferred with the specified error.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number, string>()
  const success = yield* Deferred.fail(deferred, "Operation failed")
  console.log(success) // true
})

failSync

const failSync: {
  <E>(evaluate: LazyArg<E>): <A>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, evaluate: LazyArg<E>): Effect<boolean>
}
Fails the Deferred with a lazily evaluated error.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number, string>()
  const success = yield* Deferred.failSync(deferred, () => "Lazy error")
  console.log(success) // true
})

failCause

const failCause: {
  <E>(cause: Cause<E>): <A>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, cause: Cause<E>): Effect<boolean>
}
Fails the Deferred with the specified Cause.
import { Cause, Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number, string>()
  const success = yield* Deferred.failCause(
    deferred,
    Cause.fail("Operation failed")
  )
  console.log(success) // true
})

failCauseSync

const failCauseSync: {
  <E>(evaluate: LazyArg<Cause<E>>): <A>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, evaluate: LazyArg<Cause<E>>): Effect<boolean>
}
Fails the Deferred with a lazily evaluated Cause.
import { Cause, Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number, string>()
  const success = yield* Deferred.failCauseSync(
    deferred,
    () => Cause.fail("Lazy error")
  )
  console.log(success) // true
})

Killing Deferreds

die

const die: {
  (defect: unknown): <A, E>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, defect: unknown): Effect<boolean>
}
Kills the Deferred with the specified defect.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  const success = yield* Deferred.die(
    deferred,
    new Error("Something went wrong")
  )
  console.log(success) // true
})

dieSync

const dieSync: {
  (evaluate: LazyArg<unknown>): <A, E>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, evaluate: LazyArg<unknown>): Effect<boolean>
}
Kills the Deferred with a lazily evaluated defect.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  const success = yield* Deferred.dieSync(
    deferred,
    () => new Error("Lazy error")
  )
  console.log(success) // true
})

Interrupting Deferreds

interrupt

const interrupt: <A, E>(self: Deferred<A, E>) => Effect<boolean>
Completes the Deferred with interruption. This will interrupt all fibers waiting on the value.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  const success = yield* Deferred.interrupt(deferred)
  console.log(success) // true
})

interruptWith

const interruptWith: {
  (fiberId: number): <A, E>(self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, fiberId: number): Effect<boolean>
}
Completes the Deferred with interruption using the specified FiberId.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  const success = yield* Deferred.interruptWith(deferred, 42)
  console.log(success) // true
})

Completion Status

done

const done: {
  <A, E>(exit: Exit<A, E>): (self: Deferred<A, E>) => Effect<boolean>
  <A, E>(self: Deferred<A, E>, exit: Exit<A, E>): Effect<boolean>
}
Exits the Deferred with the specified Exit value.
import { Deferred, Effect, Exit } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  yield* Deferred.done(deferred, Exit.succeed(42))

  const value = yield* Deferred.await(deferred)
  console.log(value) // 42
})

isDone

const isDone: <A, E>(self: Deferred<A, E>) => Effect<boolean>
Returns true if the Deferred has already been completed.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  
  const beforeCompletion = yield* Deferred.isDone(deferred)
  console.log(beforeCompletion) // false

  yield* Deferred.succeed(deferred, 42)
  
  const afterCompletion = yield* Deferred.isDone(deferred)
  console.log(afterCompletion) // true
})

isDoneUnsafe

const isDoneUnsafe: <A, E>(self: Deferred<A, E>) => boolean
Unsafely checks if the Deferred has been completed.

poll

function poll<A, E>(self: Deferred<A, E>): Effect<Effect<A, E> | undefined>
Returns an Effect if the Deferred has already been completed, undefined otherwise.
import { Deferred, Effect } from "effect"

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  
  const beforeCompletion = yield* Deferred.poll(deferred)
  console.log(beforeCompletion === undefined) // true

  yield* Deferred.succeed(deferred, 42)
  
  const afterCompletion = yield* Deferred.poll(deferred)
  console.log(afterCompletion !== undefined) // true
})

Unsafe Operations

doneUnsafe

const doneUnsafe: <A, E>(self: Deferred<A, E>, effect: Effect<A, E>) => boolean
Unsafely exits the Deferred with the specified effect.
import { Deferred, Effect } from "effect"

const deferred = Deferred.makeUnsafe<number>()
const success = Deferred.doneUnsafe(deferred, Effect.succeed(42))
console.log(success) // true

Integration with Effects

into

const into: {
  <A, E>(deferred: Deferred<A, E>): <R>(self: Effect<A, E, R>) => Effect<boolean, never, R>
  <A, E, R>(self: Effect<A, E, R>, deferred: Deferred<A, E>): Effect<boolean, never, R>
}
Converts an Effect into an operation that completes a Deferred with its result.
import { Deferred, Effect } from "effect"

const successEffect = Effect.succeed(42)

const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number, string>()

  const isCompleted = yield* Deferred.into(successEffect, deferred)
  const value = yield* Deferred.await(deferred)
  
  console.log(value) // 42
  return isCompleted // true
})

Common Patterns

Producer-Consumer

import { Deferred, Effect } from "effect"

const producerConsumer = Effect.gen(function*() {
  const buffer = yield* Deferred.make<Array<number>, never>()

  const producer = Effect.gen(function*() {
    const data = [1, 2, 3, 4, 5]
    yield* Deferred.succeed(buffer, data)
  })

  const consumer = Effect.gen(function*() {
    const data = yield* Deferred.await(buffer)
    return data.reduce((sum, n) => sum + n, 0)
  })

  const [, result] = yield* Effect.all([producer, consumer])
  return result // 15
})

Coordination Point

import { Deferred, Effect, Fiber } from "effect"

const coordinationExample = Effect.gen(function*() {
  const checkpoint = yield* Deferred.make<void>()

  // Multiple fibers wait for checkpoint
  const worker1 = Effect.gen(function*() {
    yield* Deferred.await(checkpoint)
    console.log("Worker 1 proceeding")
  })

  const worker2 = Effect.gen(function*() {
    yield* Deferred.await(checkpoint)
    console.log("Worker 2 proceeding")
  })

  const fiber1 = yield* Effect.forkChild(worker1)
  const fiber2 = yield* Effect.forkChild(worker2)

  // Release all workers at once
  yield* Effect.sleep("1 second")
  yield* Deferred.succeed(checkpoint, undefined)

  yield* Fiber.join(fiber1)
  yield* Fiber.join(fiber2)
})

Build docs developers (and LLMs) love