Sometimes we need a simple countdown. The next example allows us to handle each tick and abort the timer.
Task:
- Execute tick every
timeout
milliseconds - Each tick should send left seconds to listeners
- Countdown can be stopped (
abort
argument) - Countdown can’t be started if already started
function createCountdown(name, { start, abort = createEvent(`${name}Reset`), timeout = 1000 }) { // tick every 1 second const $working = createStore(true, { name: `${name}Working` }); const tick = createEvent(`${name}Tick`); const timerFx = createEffect(`${name}Timer`).use(() => wait(timeout));
$working.on(abort, () => false).on(start, () => true);
sample({ source: start, filter: timerFx.pending.map((is) => !is), target: tick, });
sample({ clock: tick, target: timerFx, });
const willTick = sample({ source: timerFx.done.map(({ params }) => params - 1), filter: (seconds) => seconds >= 0, });
sample({ source: willTick, filter: $working, target: tick, });
return { tick };}
function wait(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); });}
Usage:
const startCountdown = createEvent();const abortCountdown = createEvent();
const countdown = createCountdown("simple", { start: startCountdown, abort: abortCountdown,});
// handle each tickcountdown.tick.watch((remainSeconds) => { console.info("Tick. Remain seconds: ", remainSeconds);});
// let's startstartCountdown(15); // 15 ticks to count down, 1 tick per second
// abort after 5 secondsetTimeout(abortCountdown, 5000);