Skip to content

Signals

Signals, unlike Promises that resolve only once, have the capability to produce values asynchronously multiple times.

The constructor of Signal is described as:

js
new Signal(executor);

The executor is a function executed by the constructor, which receives two functions as parameters: next and view and returns a disposal hook:

js
new Signal((next, value) => {
  return () => {};
});

next(value)

Every time the next callback of the specified executor is called, a new value is generated, triggering re-renders the outputs of any referencing blocks.

In Genji, if the view function of a signal is not called, the new values produced by the next function will be inspected into the document. For example, to track the mouse position:

js
pointer = new Signal((next) => {
  const pointermoved = (event) => next([event.clientX, event.clientY]);
  addEventListener("pointermove", pointermoved);
  next([0, 0]);
  return () => removeEventListener("pointermove", pointermoved);
});
js
`The mouse is at <${pointer[0].toFixed(2)}, ${pointer[1].toFixed(2)}>.`;

view(element)

If the view callback is called, the return element will be displayed in the document. For example, to define a custom input:

js
name = new Signal((next, view) => {
  const input = document.createElement("input");
  const onChange = (e) => next(e.target.value);
  input.addEventListener("input", onChange);
  view(input);
  next("");
  return () => input.removeEventListener("input", onChange);
});
js
`My name is ${name}`;

Disposal hook

The disposal hook should dispose some resources the Signal allocates, say to cancel an animation loop or remove event listeners.

js
restart = Inputs.button("restart", { label: "Click me" });
js
new Signal((next, view) => {
  restart;
  let count = 0;
  const timer = setInterval(() => next(++count), 1000);
  next(count);
  return () => clearInterval(timer);
});

Global signals

There are some global signals can be accessed directly in code blocks, such as width and now.

The width signal produces the current width of page, say to draw a responsive box:

js
box(width);

The now signal produces the current timestamp:

js
now;

Say to author a smooth animation:

js
box(((Math.sin(now / 1000) + 1) * width) / 2);
js
function box(size) {
  const div = document.createElement("div");
  div.style.width = size + "px";
  div.style.height = "100px";
  div.style.background = "black";
  div.style.borderRadius = "10px";
  return div;
}

Released under the MIT License.