Docs

Wait Until

Extend a function's lifetime to run background work after the response is sent.

waitUntil lets you run async work after the response has been sent. The function stays alive until all registered tasks complete — even during scale-down or redeployment.

Use it for work that doesn't need to block the response: logging, analytics, cache updates, notifications.

Usage

import { waitUntil } from "@alienplatform/sdk"

app.post("/api/checkout", async (c) => {
  const order = await processOrder(c.req)

  waitUntil(
    (async () => {
      await analytics.track("order.completed", { orderId: order.id })
      await cache.invalidate(`user:${order.userId}:orders`)
    })()
  )

  return c.json({ orderId: order.id, status: "confirmed" })
})
async fn checkout(ctx: &AlienContext, order: Order) -> Response {
    let order_id = order.id.clone();
    let user_id = order.user_id.clone();

    ctx.wait_until(move || async move {
        analytics::track("order.completed", &order_id).await;
        cache::invalidate(&format!("user:{}:orders", user_id)).await;
    })?;

    Response::json(&json!({ "orderId": order.id, "status": "confirmed" }))
}

The response returns immediately. The background task runs to completion in the same process.

API

function waitUntil(promise: Promise<unknown>): void

Pass an already-started promise. The runtime tracks it and keeps the function alive until it resolves or rejects.

Errors inside the promise are logged to stderr. They don't affect the response (it was already sent).

fn wait_until<F, Fut>(&self, task_fn: F) -> Result<()>
where
    F: FnOnce() -> Fut + Send + 'static,
    Fut: Future<Output = ()> + Send + 'static,

Pass a closure that returns a future. The runtime spawns it immediately. Returns Err if the runtime is already shutting down.

Lifecycle

  1. Your handler calls waitUntil. The task starts running immediately.
  2. The response is sent to the caller.
  3. The function instance stays alive while tasks are pending.
  4. During graceful shutdown (scale-down, redeployment), the runtime waits for all tasks to finish before terminating.

You can call waitUntil multiple times in a single request. Each task is tracked independently — a failure in one doesn't cancel others.

When to Use

Good fit: logging, analytics, cache invalidation, sending notifications, writing audit trails, cleanup.

Not a good fit: work that must succeed before the user sees a result. If you're charging a credit card or writing a critical database record, do it before sending the response.

Known limitation

waitUntil background tasks don't currently execute in TypeScript functions on Windows. Rust functions and all other platforms (Linux, macOS) work correctly.

On this page