scmJS docs

Timers

trigscript/timers is time without a loop of your own: something every ten seconds, once in three, a cooldown, a round's clock.

import { every, after, Cooldown, Countdown, SECOND } from "trigscript/timers";

program(async () => {
  const round = new Countdown(90 * SECOND);
  const waves = every(10 * SECOND, (n) => createUnit(P8, units.ZergZergling, n, locations.Spawn));
  after(3 * SECOND, () => print("Go!", { to: AllPlayers, position: "center" }));
  const stim = new Cooldown(5 * SECOND);
  while (!round.done()) {
    if (keyPressed(P1, "T") && stim.use()) {
      for (const u of unitsOf(P1, { type: units.TerranMarine })) u.stim = 37;
    }
    await nextFrame();
  }
  waves.cancel();
  victory();
});

Time is counted in frames, 24 a second at Fastest (SECOND). every and after run their function in a task beside the program, so the program goes on at once; the task they give back stops with cancel(). Their function runs to its end within the frame. Sixteen of one every or after can run at once. Cooldown, Countdown and Stopwatch only read the game's frame counter, so there is nothing to stop.

Work spread over frames. A loop runs all its rounds within one frame. over and spread hand out a few rounds a frame instead, so that a thousand bullets or a whole map's cells cost what fifty do:

import { over } from "trigscript/timers";

program(async () => {
  const bullets: { x: number; dx: number }[] = [];
  for (let i = 0; i < 200; i++) bullets.push({ x: 0, dx: (i % 5) + 1 });
  while (true) {
    for await (const b of over(bullets, { per: 50 })) b.x += b.dx;   // fifty a frame: four frames a pass
    await nextFrame();
  }
});
Name What it is
SECOND 24: frames in a second at Fastest.
now() Frames since the game began.
every(period, fn, times?) Run fn every period frames, forever or times times; fn is handed how many times it has run. Gives a task.
after(delay, fn) Run fn once, delay frames from now. Gives a task; cancel() before then and it never runs.
over(list, { per }) The elements of list, per of them a frame, for for await.
spread(count, { per }) 0 … count − 1, per of them a frame.
new Cooldown(period) use() is true when it is ready and starts it again; ready(), left() and reset().
new Countdown(length) left(), done(), restart(length) and add(frames).
new Stopwatch() elapsed() since it was made, and reset().