Skip to content

Early Return

Sometimes you need to respond quickly to an incoming HTTP request, and then do other work after sending the response. This makes for snappier UIs for users, and some webhook platforms require responses within a couple seconds. We recommend accomplishing this today by using a queue.

How to set up a queue

In your early-returning HTTP val:

early-returning.tsRun in Val Town ↗
async function handle(request: Request) {
// Send off the relevant data a queue HTTP val.
// This `fetch` is not awaited.
fetch("https://my-queue.web.val.run", {
method: "POST",
body: req.body,
});
// Respond immediately, before the queued work is done
return Response.json("ok");
}

Your queue is another HTTP val that listens for requests and processes them in their own time. Nobody is waiting for the response from the queue, so it can take as long as it needs to:

queue.tsRun in Val Town ↗
async function handle(request: Request) {
// Do some work with the request body that takes a long time
await doSomeWork(request.body);
return Response.json("ok");
}

waitUntil

This technique is a stopgap until full support for waitUntil lands in Val Town, which will handle this case more explicitly.

Promises should otherwise be awaited

While this is a useful technique, it’s important to emphasize that, in general, you should otherwise await every Promise. If you don’t remember to use await with Promises - besides this narrow use-case, errors that occur in promises won’t be properly handled and functions may run out-of-order.

For example, if you use fetch to request some resource, but forget to await it, then it won’t throw errors when it fails, and you won’t have the values you expect to have:

export async function handle() {
try {
// Result will be an opaque Promise, not a useful value.
const result = fetch("https://google.com/");
} catch (e) {
// Errors will never be caught here because
// fetch is not awaited.
handleError(e);
}
}

HTTP Val Lifecycle

When your HTTP val receives a request, we spin up a process to serve it. We keep this process alive for some amount of time after the response is sent to be ready for any other requests that might come its way. After some time of inactivity, your HTTP process is terminated. This means that if you fire off Promises without awaiting them, they may or may not finish before the val times out, depending on how long they take and whether the val is being kept alive to serve other traffic. The safest path is to await all Promises or make yourself a queue.