2

Summary

I'm currently working in an environment with around 14+ stockCharts on a given page (each with 20,0000+ unique data points, some a lot more). I have certain global "toggles" that affect, for instance, which series are displayed, chart options, or axis extremes (on all charts). Given that only one chart is visible to the user at a time, I would like to treat the updating of charts like a Queue, where I update the one chart first, then another second, etc.

My goal: call Highcharts.Chart.redraw() sequentially in a loop (i.e., call redraw on each chart in the page, one at a time - so that the first chart in my list of charts is redrawn first). Note that the ordering of the list of charts is dynamically created and already taken care of.

Background

So far I've read around a good amount about JavaScript promises, which I'ved used in the past for chaining ajax calls. As is discussed in the following links:

I've also read some more general questions on here related to Promises in loops:

Code / Details

I have tried numerous solutions, and believe the issue revolves around making the Highcharts redraw funcion - or the actions calling it (like Chart.update(), Series.setVisible(), Axis.setExtremes()) - return a promise.

I've uploaded something of a shell here which allows you to trigger an event, and see that each chart is essentially updated at once.


For instance, something like the following:

$(Highcharts.charts).each(function(i, chart) {
    console.log(i);
    // decide what to do with the chart
    // update Series, Chart options, Axis extremes... etc.
    chart.redraw(); // call redraw at the end
})

would output:

> 1, 2, 3, ..., n // pause, then charts would redraw()

It's probably important to mention that while the above appears responsive, in my environment there is probably somewhere between a 2-5 second delay between clicking a toggle, and the redraw event finishing for the charts.

What I can't figure out is how to make the loop wait for each chart to finish its redraw before proceeding to the next chart in the list.

Any help that can be provided would be greatly appreciated.

laventnc
  • 175
  • 13
  • 1
    Not sure if this is a viable option for you but instead of a loop you could fire an event once one draw finishes, the parent could capture the event and initiate the redraw for the next in line. – Max Mar 27 '19 at 22:03
  • yeah that's what I was thinking, like pass in the list of remaining charts to in the event arguments to the parents redraw() method. The main problem with that (if I'm understanding you correctly) is that the relationships between the charts and toggles would require some level of mapping that I was hoping to avoid. I also figured this would be a good opportunity to learn about Promises as I've until recent tended to avoid them. – laventnc Mar 27 '19 at 22:08

2 Answers2

2

If I understand what you mean, you can achieve it by wrapping the code you have in the trigger in a setTimeout (with a timeout value of 0), in turn wrapped in a new Promise

using async/await it would be something like

$('#trigger').click(async (e) => {
    for (let chart of Highcharts.charts) {
        await new Promise(resolve => setTimeout(() => {
            chart.series.forEach(series => series.setVisible(undefined, false));
            chart.redraw();
            resolve();
        }, 0));
    }
});
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
1

GENERIC SOLUTION

Generally if you want to sequentially chain promises in a loop you can use the following format

function promiseChain(i) {
    return new Promise(resolve => {
        /* DO ASYNC STUFF */

        // Resolve with some result if the next item in the chain needs it
        return resolve("OK");
    });
}

let p = Promise.resolve(null);

for (let i = 0; i < 5; i++) {
    p = p.then(prevResult => {
        if (prevResult) {
            // Do stuff with prevResult if needed
        }

        // Chain next item
        return promiseChain(i);
    });
}

Example with logs

This will log numbers 0-4, one at a time, waiting one second between each.

function promiseChain(i) {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(i);
            return resolve("OK");
        }, 1000)
    });
}

let p = Promise.resolve(null);

for (let i = 0; i < 5; i++) {
    p = p.then(prevResult => {
        if (prevResult) {
            // Do stuff with prevResult if needed
        }

        return promiseChain(i);
    });
}
kht
  • 580
  • 2
  • 8
  • so i tried your solution and i couldnt get it to wait for the chart to redraw before proceeding... for instance here: https://jsfiddle.net/nlaventis/do48cpsb/49/ seems to be updating all at once – laventnc Mar 28 '19 at 13:39