The Bnlang Event Loop
Every Bnlang program runs on a single event loop, provided by libuv. The loop is what makes async I/O work: while one request is waiting on disk or the network, the loop keeps spinning and runs other callbacks that are ready.
How It Works
- Your script runs top to bottom. Synchronous statements execute immediately.
- Async calls (e.g.
io.read_file_async,timers.set, network ops) hand work off to libuv and return without waiting. - When your script finishes its top-level code, the runtime hands control to the event loop.
- As each operation completes, libuv queues the matching callback. The loop picks them up one at a time and runs them on the main thread.
- The process exits when there is no more pending work — no live timers, no open streams, no in-flight requests.
Example: Timers and Async I/O
The output below shows the loop in action. Notice that the synchronous print calls run first, and then the loop drains the scheduled callbacks.
import "timers" as timers;
import "io" as io;
print("a");
timers.set(0, function () {
print("timer fired");
});
io.read_file_async("greeting.txt", function (err, data) {
if (err != null) { print("read err:", err); return; }
print("file:", data);
});
print("b");
// Output (file order depends on disk timing):
// a
// b
// timer fired
// file: ...
What's Not Here
Bnlang's event loop is deliberately simple. A few things you might expect from other runtimes don't exist:
- No microtask queue — there are no Promises, so there is no
then/awaitmicrotask ordering. Callbacks run in the order their I/O completes. - No
process.nextTickorsetImmediate— usetimers.set(0, fn)to defer until the next loop turn. - No background threads visible to scripts — heavy I/O uses libuv's thread pool internally, but your script always runs on the main thread.
Best Practices
- Keep callbacks short. They share the loop with every other in-flight request.
- For CPU-heavy work, slice it into chunks separated by
timers.set(0, ...)so other callbacks get a turn. - Always close streams and clear interval timers you no longer need — they will keep the loop (and your process) alive.