Best way to break a promise?

I have a lot of historical time-series data that is network restricted and comes into Javascript slowly via a server-side send. I arrange the data in a sort of binary decomposition (e.g. all odd dates at midnight, then all even dates at midnight, then all odd dates at noon, etc) and interpolate all missing data. I send a Plotly Update each time I receive another chunk and the graph animates nicely as the data downloads and the interpolation becomes more precise… Sometimes though, I get the data quickly and the updates can be slow, so the update “promises” can stack up with some of the earlier promises now stale. I’d like to delete pending promises and go straight to the latest promise. What is best practice?

What I did have was a global variable PLOTTER tagged to the webpage DOM and a local variable ‘UPD’ defined within onmessage() and UPD gets updated by the SSE. Inside that onmessage routine, I then make the call to initiate a promise with Plotly.update(PLOTTER, UPD,…). UPD has x-axis with all of the times and each point on y axis is either real or interpolated (until the new data comes in).

One approach could be to maintain an index table what chunks have arrived and instead of calling update directly from onmessage(), call an ancillary function that checked the queue before making a decision to call Plotly.

I’m also confused what happens if I make UPD global. If UPD is updated after the call to Plotly.update has issued but not completed then does plotly get the most recent data, or does it get the data that was in UPD at the time of the promise??

I think I have a similar problem.
I am keeping track of traces in a global variable adding and removing them as the user interacts with the web page. If a user makes a request that simultaneously removes a bunch of traces and then adds more, my global tracking assumes sequential execution, while Plotly seems to run additions and removals asynchronously. This results in:
Error: The layer 'plotly-trace-layer-f87869-symbol' does not exist in the map's style and cannot be removed
I could overcome this if I could force Plotly to finish rendering the removals, then begin additions.
Is this possible?

I solved how to do the “delete intermediate request”. If you Google “javascript ordered promises” then you will see a few ideas on how to make one promise always come before the next, and those solutions typically involve building a stack of yet-to-be-launched promises. So the tweak I had to make was to carefully blow off old promises when adding new ones to the stack.

That said, the result proved a bit disappointing for me. My queue was almost always empty. Plotly is just too damned fast :slight_smile: My users experience was essentially unchanged after I spent quite a while chasing through the details. I had thought my system was waiting for Plotly to “react” to the incremental updates, but the slow part turned out to be the data transfer.

On your side it seems like ordered promises could help. Or simpler? Note that if one promise is the result of a “then”, i.e., chained promises, then they are always forced to iterate in sequence.

Regards, T.

Thank you for this guidance! I indeed solved the problem by chaining the promises.
I will try to contribute an example to Plotly JS docs, meanwhile, this may be helpful to those finding themselves in “my shoes” of two days ago.

Overview

We have several groups of objects (buses, lines, …) that are displayed on a map. A user choses what is displayed and our UI allows for stuff to be added and removed multiple times and in any order. We started out by using a global variable ixs that kept track of indices of traces shown on the map and, within it, had a variable ixs.i which was keeping track of the next available index. Once we upgraded from plotly-latest.min.js (which is misleadingly named as it is only v1.58.5) to plotly-2.22.0.min.js, the additions and deletions of traces started to race asynchronously and our ixs.i would get out of sync with the figure. This created a need to chain the Plotly calls such that all the bookkeeping associated with addTraces or deleteTraces gets fully handled before another set of elements gets placed on or removed from the map.

See the last section for the specific guidance I welcome.

Example code

const ixs= {
  p: Promise.resolve(), // placeholder for a promise chain
  buses: {start:null; end:null, shown:false}, // tracking vars for bus traces
  lines: {start:null, end:null, shown:false}, // tracking vars for line traces
  // several more
}
// ... 
// busVals is an array of jsons holding various variables that are to be displayed at bus locations
function addBuses(selBusVar) {
  // prepare 
  ixs.p = ixs.p.then(() => {
    let nrecords = Object.keys(busVals[selBusVar]).length; // number of bus traces that will be added
    let gid = document.getElementById("map");
    ixs.buses.start=gid.data.length; // start is set to next available trace index 
    ixs.buses.end=ixs.buses.start+nrecords-1; 
    let busix = [...Array(nrecords).keys()].map(x=>x+ixs.buses.start); // trace indices for use by addTraces
    ixs.buses.shown=true;
    return Plotly.addTraces(gid, busVals[selBusVar], busix);
  });
//...
function removeBuses() {
  ixs.p = ixs.p.then(() => {
    let gid = document.getElementById("map");
    let busix = [...Array(ixs.buses.end-ixs.buses.start+1).keys()];
    busix = busix.map(x=>(x+ixs.buses.start));
    updateOthers('buses', busix.length); // this decrements the start and end indices of other traces placed above bus traces by the number of removed bus traces
    ixs.buses.shown=false;
    ixs.buses.start=null;
    ixs.buses.end=null;
    return Plotly.deleteTraces(gid, busix);
    })
}
//...
function updateOthers(layer, delta) {
  if (ixs.lines.start >= ixs[layer].end) {
    ixs.lines.start-=delta;
    ixs.lines.end -= delta;
  }
  // all other objects... 
  // This function can be made a lot "more DRY"!
}

Guidance welcome

  • Can this be made completely asynchronous while preserving the intent to make minimal incremental changes to the map?
  • Did I set up the promise chain correctly? I have an uneasy feeling that it might endlessly accumulate and eventually overflow. In other words, I am not sure if what I set up works as stack or as an array that keeps accumulating.

Sample map