std/loop

The event loop waits for many things at once and runs a callback for each as it happens, one at a time: a callback runs to its end before another starts. It waits on timers, signals, processes, and watched files; network connections come next.

import "std/loop"

Timer clock = every(1000, func() do
    print("tick")
end)
after(3500, func() do
    cancel(clock)
end)
run_loop()      // tick, tick, tick, and then it returns
Function Result
run_loop() runs callbacks as they become due, until nothing is left to wait for or stop_loop() is called
stop_loop() makes run_loop return once the callback running now ends
run_once(timeout_ms) runs what is due, or waits up to timeout_ms for something to be, for a program that drives the loop itself; says whether anything is left
loop_time() the loop's time in milliseconds, on a clock that never goes back, as it was when the running callbacks became due
after(ms, callback) a Timer that runs callback once, ms milliseconds from now
every(ms, callback) a Timer that runs callback every ms milliseconds
cancel(timer) stops a timer
reschedule(timer, ms) makes a timer run ms milliseconds from now, also one that already ran
unref(timer) lets the loop end while the timer waits
call_soon(callback) runs callback right after the callback running now
on_idle(callback) runs callback once, the next time the loop has nothing else to do

An every timer counts from when it started, not from when each callback ran, so it does not drift; a time the loop was too busy for is skipped. Rescheduling a timer each time something happens makes a debounce: its callback runs once, after the last of them. While it waits, the loop sleeps until the next timer is due, so it uses no processor time. A runtime error in a callback ends the program with its trace, as anywhere else.

Signals

on_signal(name, callback) runs callback each time the program gets a signal, instead of what the signal would do, such as ending the program, so a server can finish its work on Ctrl+C:

on_signal("interrupt", func() do
    print("saving before leaving")
    stop_loop()
end)

The signals are "interrupt" (Ctrl+C), "terminate", "hangup", "user1", "user2", "child", "window_change", and "pipe". A callback for a signal does not keep the loop running by itself. ignore_signal(name) makes the program take no notice of one and forgets its callbacks, and send_signal(pid, name) sends one to a process, giving a Result; process_id() is this program's id.

On Windows, interrupt comes from Ctrl+C and Ctrl+Break, also to a program that was started ignoring Ctrl+C, as Windows lets a program pass that on to those it starts; hangup comes when the console window is closed, and terminate when the user logs off or the system shuts down, which leaves the program a few seconds for its callbacks; the other signals never come there. Windows can send interrupt only to a process started in a process group of its own, as Ctrl+Break, and terminate, which ends the process at once; sending another gives an ERROR of kind "unsupported".