taskle

Types

pub type Error {
  Timeout
  Crashed(reason: String)
  NotOwner
  NotReady
}

Constructors

  • Timeout

    The task didn’t complete within the specified timeout.

  • Crashed(reason: String)

    The task process crashed with the given reason.

  • NotOwner

    Attempted to await or cancel a task from a different process than the one that created it.

  • NotReady

    The task is not ready yet (used by yield function).

Represents the settled state of a task (for all_settled).

pub type SettledResult(a) {
  Fulfilled(value: a)
  Rejected(error: Error)
}

Constructors

  • Fulfilled(value: a)

    The task completed successfully with the given value.

  • Rejected(error: Error)

    The task failed with the given error.

A task represents an asynchronous computation running in a separate BEAM process. Tasks are created with async and their results are retrieved with await. Only the process that created a task can await its result or cancel it.

pub opaque type Task(a)
pub type TaskMessage(a) {
  TaskResult(value: a)
}

Constructors

  • TaskResult(value: a)

Values

pub fn all_settled(
  tasks: List(Task(a)),
  timeout: Int,
) -> Result(List(SettledResult(a)), Error)

Waits for all tasks to complete regardless of success or failure (analog of Promise.allSettled).

Returns the results of all tasks, whether they succeeded or failed. Unlike try_await_all, this function never cancels tasks early - it waits for all tasks to complete.

Examples

let task1 = taskle.async(fn() { 42 })
let task2 = taskle.async(fn() {
  // This will crash
  panic as "oops"
})
let task3 = taskle.async(fn() { "hello" })

case taskle.all_settled([task1, task2, task3], 5000) {
  Ok([Fulfilled(42), Rejected(Crashed("oops")), Fulfilled("hello")]) -> {
    io.println("All tasks completed")
  }
  Error(taskle.Timeout) -> io.println("Some tasks timed out")
}
pub fn async(fun: fn() -> a) -> Task(a)

Creates an asynchronous task that runs the given function in a separate process.

The task is unlinked from the calling process, meaning that if the task crashes, it won’t cause the calling process to crash. Only the process that creates the task can await its result or cancel it.

Examples

let task = taskle.async(fn() {
  // This runs in a separate process
  process.sleep(1000)
  42
})
pub fn async_unlinked(fun: fn() -> a) -> Task(a)

Creates an asynchronous task that is not linked to the calling process.

This is identical to async, but provided for clarity when you specifically want to emphasize that the task is unlinked. The task DOES send its result back and can be awaited, unlike tasks created with start.

Key difference from start: Tasks created with async_unlinked send their result back and can be awaited. Use this when you need the result but want to emphasize the unlinked nature, or when you might await the task later.

Examples

let task = taskle.async_unlinked(fn() {
  // This task won't affect the parent process if it crashes
  expensive_computation()
})

// You can still await the result
case taskle.await(task, 5000) {
  Ok(result) -> use_result(result)
  Error(_) -> handle_error()
}
pub fn await(task: Task(a), timeout: Int) -> Result(a, Error)

Waits for a task to complete with a timeout in milliseconds.

Only the process that created the task can await its result. If called from a different process, returns Error(NotOwner).

Examples

case taskle.await(task, 5000) {
  Ok(value) -> io.println("Success: " <> int.to_string(value))
  Error(taskle.Timeout) -> io.println("Task timed out after 5 seconds")
  Error(taskle.Crashed(reason)) -> io.println("Task failed: " <> reason)
  Error(taskle.NotOwner) -> io.println("Cannot await task from different process")
}
pub fn await2(
  task1: Task(t1),
  task2: Task(t2),
  timeout: Int,
) -> Result(#(t1, t2), Error)

Waits for two tasks to complete and returns a tuple of their results.

Both tasks must complete successfully within the timeout, otherwise an error is returned. If any task fails, all remaining tasks are cancelled.

Examples

let task1 = taskle.async(fn() { 42 })
let task2 = taskle.async(fn() { "hello" })

case taskle.await2(task1, task2, 5000) {
  Ok(#(num, str)) -> {
    // num = 42, str = "hello"
    io.debug(#(num, str))
  }
  Error(taskle.Timeout) -> io.println("Tasks timed out")
  Error(taskle.Crashed(reason)) -> io.println("A task crashed: " <> reason)
}
pub fn await3(
  task1: Task(t1),
  task2: Task(t2),
  task3: Task(t3),
  timeout: Int,
) -> Result(#(t1, t2, t3), Error)

Waits for three tasks to complete and returns a tuple of their results.

All tasks must complete successfully within the timeout, otherwise an error is returned. If any task fails, all remaining tasks are cancelled.

Examples

let task1 = taskle.async(fn() { 42 })
let task2 = taskle.async(fn() { "hello" })
let task3 = taskle.async(fn() { True })

case taskle.await3(task1, task2, task3, 5000) {
  Ok(#(num, str, bool)) -> {
    // num = 42, str = "hello", bool = True
    io.debug(#(num, str, bool))
  }
  Error(taskle.Timeout) -> io.println("Tasks timed out")
  Error(taskle.Crashed(reason)) -> io.println("A task crashed: " <> reason)
}
pub fn await4(
  task1: Task(t1),
  task2: Task(t2),
  task3: Task(t3),
  task4: Task(t4),
  timeout: Int,
) -> Result(#(t1, t2, t3, t4), Error)

Waits for four tasks to complete and returns a tuple of their results.

All tasks must complete successfully within the timeout, otherwise an error is returned. If any task fails, all remaining tasks are cancelled.

Examples

let task1 = taskle.async(fn() { 42 })
let task2 = taskle.async(fn() { "hello" })
let task3 = taskle.async(fn() { True })
let task4 = taskle.async(fn() { 3.14 })

case taskle.await4(task1, task2, task3, task4, 5000) {
  Ok(#(num, str, bool, float)) -> {
    // num = 42, str = "hello", bool = True, float = 3.14
    io.debug(#(num, str, bool, float))
  }
  Error(taskle.Timeout) -> io.println("Tasks timed out")
  Error(taskle.Crashed(reason)) -> io.println("A task crashed: " <> reason)
}
pub fn await5(
  task1: Task(t1),
  task2: Task(t2),
  task3: Task(t3),
  task4: Task(t4),
  task5: Task(t5),
  timeout: Int,
) -> Result(#(t1, t2, t3, t4, t5), Error)

Waits for five tasks to complete and returns a tuple of their results.

All tasks must complete successfully within the timeout, otherwise an error is returned. If any task fails, all remaining tasks are cancelled.

Examples

let task1 = taskle.async(fn() { 42 })
let task2 = taskle.async(fn() { "hello" })
let task3 = taskle.async(fn() { True })
let task4 = taskle.async(fn() { 3.14 })
let task5 = taskle.async(fn() { [1, 2, 3] })

case taskle.await5(task1, task2, task3, task4, task5, 5000) {
  Ok(#(num, str, bool, float, list)) -> {
    // num = 42, str = "hello", bool = True, float = 3.14, list = [1, 2, 3]
    io.debug(#(num, str, bool, float, list))
  }
  Error(taskle.Timeout) -> io.println("Tasks timed out")
  Error(taskle.Crashed(reason)) -> io.println("A task crashed: " <> reason)
}
pub fn await_forever(task: Task(a)) -> Result(a, Error)

Waits for a task to complete without a timeout.

Only the process that created the task can await its result. If called from a different process, returns Error(NotOwner). Will wait indefinitely until the task completes or crashes.

Examples

case taskle.await_forever(task) {
  Ok(value) -> io.println("Success: " <> int.to_string(value))
  Error(taskle.Crashed(reason)) -> io.println("Task failed: " <> reason)
  Error(taskle.NotOwner) -> io.println("Cannot await task from different process")
}
pub fn cancel(task: Task(a)) -> Result(Nil, Error)

Cancels a running task.

Only the process that created the task can cancel it. If called from a different process, returns Error(NotOwner).

Examples

let task = taskle.async(fn() {
  process.sleep(10_000)
  "done"
})

// Cancel the task
case taskle.cancel(task) {
  Ok(Nil) -> io.println("Task cancelled")
  Error(taskle.NotOwner) -> io.println("Cannot cancel task from different process")
}
pub fn parallel_map(
  list: List(a),
  fun: fn(a) -> b,
  timeout: Int,
) -> Result(List(b), Error)

Processes a list of items in parallel, applying the given function to each item.

Returns when all tasks complete or when any task fails/times out. If any task fails, all remaining tasks are cancelled.

Examples

let numbers = [1, 2, 3, 4, 5]

case taskle.parallel_map(numbers, fn(x) { x * x }, 5000) {
  Ok(results) -> {
    // results = [1, 4, 9, 16, 25]
    io.debug(results)
  }
  Error(taskle.Timeout) -> io.println("Some tasks timed out")
  Error(taskle.Crashed(reason)) -> io.println("A task crashed: " <> reason)
}
pub fn pid(task: Task(a)) -> process.Pid

Returns the process ID of the task’s underlying BEAM process.

Useful for debugging or process monitoring.

Examples

let task = taskle.async(fn() { 42 })
let process_id = taskle.pid(task)
io.debug(process_id)
pub fn race(
  tasks: List(Task(a)),
  timeout: Int,
) -> Result(a, Error)

Waits for the first task to complete (analog of Promise.race).

Returns the result of the first task to complete, whether successful or failed. All other tasks are cancelled when the first one completes.

Examples

let task1 = taskle.async(fn() {
  process.sleep(1000)
  "slow"
})
let task2 = taskle.async(fn() {
  process.sleep(100)
  "fast"
})

case taskle.race([task1, task2], 5000) {
  Ok("fast") -> io.println("Task2 won the race")
  Error(taskle.Timeout) -> io.println("All tasks timed out")
  Error(taskle.Crashed(reason)) -> io.println("First task to complete crashed: " <> reason)
}
pub fn shutdown(
  task: Task(a),
  timeout: Int,
) -> Result(Nil, Error)

Shuts down a task gracefully with a timeout.

This function attempts to shut down a task more gracefully than cancel. It first removes monitoring of the process, then kills it. If the task doesn’t shut down within the timeout, it returns an error.

Only the process that created the task can shut it down. If called from a different process, returns Error(NotOwner).

Examples

let task = taskle.async(fn() {
  process.sleep(10_000)
  "done"
})

// Shutdown the task with a 1 second timeout
case taskle.shutdown(task, 1000) {
  Ok(Nil) -> io.println("Task shut down gracefully")
  Error(taskle.Timeout) -> io.println("Task didn't shut down in time")
  Error(taskle.NotOwner) -> io.println("Cannot shutdown task from different process")
}
pub fn start(fun: fn() -> a) -> Task(a)

Creates an asynchronous task for side effects that doesn’t need to be awaited.

This is useful for fire-and-forget operations where you don’t need the result. The task runs in an unlinked process and won’t affect the parent process if it crashes.

Key difference from async_unlinked: Tasks created with start do NOT send their result back, making them truly fire-and-forget. While you can technically call await on them, it will never receive a result. Use start for side effects like logging, cleanup, or background processing where you don’t care about the return value.

Examples

taskle.start(fn() {
  // Log something or perform side effects
  io.println("Background task completed")
  cleanup_temp_files()
})
pub fn try_await_all(
  tasks: List(Task(a)),
  timeout: Int,
) -> Result(List(a), Error)

Waits for all tasks to complete with a timeout.

Returns when all tasks complete or when any task fails/times out. If any task fails, all remaining tasks are cancelled.

Examples

let task1 = taskle.async(fn() { 42 })
let task2 = taskle.async(fn() { "hello" })
let task3 = taskle.async(fn() { True })

case taskle.try_await_all([task1, task2, task3], 5000) {
  Ok([a, b, c]) -> {
    // a = 42, b = "hello", c = True
    io.debug([a, b, c])
  }
  Error(taskle.Timeout) -> io.println("Some tasks timed out")
  Error(taskle.Crashed(reason)) -> io.println("A task crashed: " <> reason)
}
pub fn yield(task: Task(a)) -> Result(a, Error)

Checks if a task has completed without blocking.

Returns Ok(value) if the task has completed, Error(NotReady) if it’s still running, or other errors if the task crashed or ownership check fails.

Examples

case taskle.yield(task) {
  Ok(value) -> io.println("Task completed: " <> int.to_string(value))
  Error(taskle.NotReady) -> io.println("Task still running")
  Error(taskle.Crashed(reason)) -> io.println("Task failed: " <> reason)
  Error(taskle.NotOwner) -> io.println("Cannot check task from different process")
}
Search Document