megaplot icon indicating copy to clipboard operation
megaplot copied to clipboard

Consider adding a queue API for successive binds

Open RyanMullins opened this issue 3 years ago • 1 comments

tl;dr -- Consider adding a Selection.queue() API that would effectively enqueue multiple .bind()-equivalents and guarantee that each .bind()-equivalent is completed before processing the next one.

Background

In LIT, I'm using multiple Selections to act as proxies for layers in the Scene. One of these Selections is always bound to the entire dataset, and the other Selections are bound to com subset of the dataset (not that it matters for this issue, but one Selection can contain [0, N] datums and the other 3 can contain [0, 1] datums). For the Selections containing subsets, I assumed that calling .bind() with an empty array would mark all existing datums (and their associated Sprites) for removal from the Scene, effectively clearing the Selection and allowing me to make a successive call to .bind() with the new contents that would render the data I now cared about in the Scene. Some example code below.

const data = [/* let's assume this is populated */];

const container = document.querySelector('some_query');
const scene = new Scene({container});
const allData = scene.createSelection();
const someData = scene.createSelection();

allData.bind(data);

function update(someSentinel) {
  someData.bind([]);
  const interestingData = data.filter(d => /* some predicate */);
  someData.bind(interestingData);
}

This assumption was incorrect because of how the Selection.bind() uniquely identifies binding tasks. Calls to .bind() are transformed into bindingTasks that are uniquely identified by the Selection on which .bind() is being called. In the example above, the successive calls to .bind() effectively overwrite each other in the scheduler because they process before the next animation frame happens.

Desired Solution

A complement to the .clear() API proposed in #11 that would also address the problem described above would be to add a .queue() API that effectively enqueues a series of .bind()-equivalents for that Selection, and ensures that each .bind()-equivalent completes before processing the next one. This could be useful for processing complex, successive transforms on bound data.

function update(someSentinel) {
  someData.queue([]);    // Enqueues a `.bind()`-equivalent that removes all content from the Selection
  const interestingData = data.filter(d => /* some predicate */);
  someData.queue(interestingData);    // Enqueues a `.bind()`-equivalent that binds interestingData to the Selection
}

RyanMullins avatar May 10 '22 21:05 RyanMullins

Agreed on the utility of a .clear() method. This seems to be a common enough case to make an API for it.

In the meantime, one workaround is to .bind() the empty array, then drop the Selection and make a new one:

const data = [/* let's assume this is populated */];

const container = document.querySelector('some_query');
const scene = new Scene({container});
const allData = scene.createSelection();
let someData = scene.createSelection();  // Changed from const to let.

allData.bind(data);

function update(someSentinel) {
  someData.bind([]);
  someData = scene.createSelection();  // Creating a new Selection here.
  const interestingData = data.filter(d => /* some predicate */);
  someData.bind(interestingData);
}

jimbojw avatar May 11 '22 13:05 jimbojw