Using signals to manage reactivity
How a program responds to variable data or user interactions is one of the fundamental problems of programming. If we desire to solve the issue in a declarative manner, signals may be a viable approach.
import { S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
// α — `signal` represents a reactive value
const signal= S(0);
// β — just reacts on signal changes
S.on(signal, console.log);
// γ — just updates the value
signal(signal()+1);
setInterval(()=> signal(signal()+1), 5000);
# Introducing signals
Using signals, we split program logic into the three parts. Firstly (α), we create a variable (constant) representing reactive value. Somewhere later, we can register (β) a logic reacting to the signal value changes. Similarly, in a remaining part (γ), we can update the signal value.
All this is just an example of Event-driven programming and Publish–subscribe pattern (compare for example with fpubsub library). All three parts can be in some manner independent and still connected to the same reactive entity.
Signals are implemented in the library as functions. To see current value of signal, just call it without any arguments console.log(signal())
. To update the signal value, pass any argument signal('a new value')
. For listenning the signal value changes, use S.on(signal, console.log)
.
Similarly to the on
function to register DOM events listener. You can use AbortController
/AbortSignal
to off/stop listenning. For representing “live” piece of code computation pattern:
import { S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js";
const signal= S(0);
// computation pattern
const double= S(()=> 2*signal());
const ac= new AbortController();
S.on(signal, v=> console.log("signal", v), { signal: ac.signal });
S.on(double, v=> console.log("double", v), { signal: ac.signal });
signal(signal()+1);
const interval= 5000;
const id= setInterval(()=> signal(signal()+1), interval);
ac.signal.addEventListener("abort",
()=> setTimeout(()=> clearInterval(id), 2*interval));
setTimeout(()=> ac.abort(), 3*interval)
Mnemonic
S(<value>)
— signal: reactive valueS(()=> <computation>)
— signal: reactive value dependent on calculation using other signalsS.on(<signal>, <listener>[, <options>])
— listen to the signal value changesS.clear(...<signals>)
— off and clear signalsS(<value>, <actions>)
— signal: pattern to create complex reactive objects/arraysS.action(<signal>, <action-name>, ...<action-arguments>)
— invoke an action for given signal