Observable Framework 1.0.0 GitHub

JavaScript: Generators

Values that change over time, such as interactive inputs and animation parameters, can represented as async generators. When a top-level generator is declared, code in other blocks sees the generator’s latest yielded value and runs whenever the generator yields a new value.

For example, here is a generator that increments once a second:

const j = (async function* () {
  for (let j = 0; true; ++j) {
    yield j;
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
})();

The value of j is: .

The value of j is: ${j}.

If the generator is synchronous, the generator will yield every animation frame, which is typically 60 frames per second:

const i = (function* () {
  for (let i = 0; true; ++i) {
    yield i;
  }
})();

The value of i is: .

The value of i is: ${i}.

As another example, you can use the built-in Generators.observe to represent the current pointer coordinates:

const pointer = Generators.observe((change) => {
  const pointermoved = (event) => change([event.clientX, event.clientY]);
  addEventListener("pointermove", pointermoved);
  change([0, 0]);
  return () => removeEventListener("pointermove", pointermoved);
});

Pointer is: .

Pointer is: ${pointer.map(Math.round).join(", ")}.

Here is a WebSocket that listens for Blockchain transactions:

const socket = new WebSocket("wss://ws.blockchain.info/inv");
invalidation.then(() => socket.close());
socket.addEventListener("open", () => socket.send(JSON.stringify({op: "unconfirmed_sub"})));
const message = Generators.observe((change) => {
  const messaged = (event) => change(JSON.parse(event.data));
  socket.addEventListener("message", messaged);
  return () => socket.removeEventListener("message", messaged);
});
message.x // the most recently reported transaction

And here’s an HTML input element using Generators.input:

const nameInput = display(document.createElement("input"));
const name = Generators.input(nameInput);
name