This page explains how Varn runs your code and the rules to follow so you stay responsive instead of blocking.
Your script, your HTTP handlers, timers, and promise results all run together, one at a time. This keeps things simple and predictable.
The trade-off is that if you do something slow directly, like a long loop or a blocking read, nothing else runs until it finishes. The fix is to move slow work to the background and get the result back through a promise.
Anything that does slow work returns a promise instead of blocking. Call :await() to pause until it finishes and get the value:
local fs = require("fs")
local async = require("async")
async.spawn(function()
local text, err = fs.readFile("notes.txt"):await()
if err then
print("could not read:", err)
return
end
print(text)
end)The method :await() pauses the current coroutine and lets everything else keep running. When the work finishes, your coroutine resumes right after the :await().
The method :await() only works inside a coroutine. Varn opens one for you in async.run(fn), async.spawn(fn), and inside an HTTP handler. At the top level of a plain script there is no coroutine to pause, so call async.run(fn) and put your code inside fn. A promise settles exactly once, either resolved or rejected. Later settles are ignored.
- The function
async.sleep(ms)returns a promise that resolves aftermsmilliseconds without blocking. - The function
async.spawn(fn)runsfnas a background coroutine so it can:await()promises. Any error that escapesfnends the process with a non-zero exit code. - The function
async.run(fn)runsfnas the program's async entry point. The process exits cleanly whenfnreturns, or with a non-zero status if an uncaught error escapes. Use it at the top level of a script instead ofasync.spawnwhen you want the script to end withfn. - The function
async.promise(fn)runsfnas a coroutine and returns a promise that resolves with its return value or rejects with its error. Use it to turn an async helper into something the rest of your code can:await().
Combinators build on these primitives and each return a promise: async.all, async.allSettled, async.race, async.any, async.timeout, and async.mapLimit. See lua-api/async.md for their exact contracts.
local async = require("async")
async.spawn(function()
print("start")
async.sleep(500):await() -- Everything else stays free during this pause
print("half a second later")
end)
print("this line runs first, before the sleep finishes")Do:
- Use
:await()for I/O (files, network, sockets, zip). It keeps the server responsive. - Wrap independent work in
async.spawnso the pieces can interleave. - Read the second return of
:await()for errors, as inlocal value, err = p:await().
Don't:
- Don't run heavy CPU work or a busy loop directly. It blocks every other request until it ends. Keep handlers short, or break the work up.
- Don't fake waiting with
os.executeor a manual busy-loop. Useasync.sleepor:await(). - Don't call
:await()at the top level of a script. Wrap your code inasync.run(one-shot scripts) orasync.spawn(background tasks).
The same code runs, but anything that needs a listening socket or native crypto is unavailable, and outbound HTTP is routed through the browser. The function async.sleep and promises behave the same as elsewhere. Treat :isDone() as a hint, never as a lock.