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.