Concurrency and async calls

Run async work inside a function call, and know which state concurrent calls of your app share.

Docs menu

Async inside one call

db is synchronous: a read or write returns before the next line runs, with no event-loop hop. fetch, timers and promises you create are ordinary JavaScript: await them, chain .then, or return a Promise from an async handler. queueMicrotask runs on the engine's own microtask queue, as in a browser.

export default {
  async fetch(request, env, ctx) {
    const upstream = await fetch("https://api.example.com/data");
    db.events.insert({ at: Date.now() });
    ctx.waitUntil(fetch("https://hooks.example.com/seen", { method: "POST" }));
    return Response.json(await upstream.json());
  },
};

ctx.waitUntil(promise) is not a background task: every waitUntil promise is awaited after your handler returns and before the response is sent to the client. setTimeout and setInterval answer a numeric id for clearTimeout/clearInterval; a call's timers run on its own CPU and wall budget and are cleared when the call ends, so a timer never fires after its call has finished.

Calls run concurrently

Each HTTP request, cron run, queue delivery or direct function call is its own call, and many calls of the same app run at once: 512 by default, or the app's own cf_concurrent_limit (GET /v1/_limits reads it). A call past that limit is refused with HTTP 429. A call queued past the worker's own queue depth is refused with HTTP 503. See Limits for every related limit and error.

State is not shared safely

Module-level variables and globalThis persist on the worker thread that happens to run a call, not per call: see State between calls. Treat anything outside db, kv or records as a cache that may be stale, empty, or shared with a concurrent call you did not expect.

One object, one call at a time

A durable object is the opposite of the model above: its calls queue on a mailbox and run strictly one at a time, so its in-memory fields are safe to read and write without a lock. Use an object, not module state, when two calls must never interleave on the same data.