Search code examples
jqueryajaxsynchronizationc3

Synchronous call to load json files before draw the chart with C3


I have the following situation: Three different JSON files need to be loaded before firing the function to generate the chart. Here's the code:

<div id="jsonloading"></div>
<div id="chartc3"></div>
<script>
    var scene;
    var surchargedata;
    var ratiodata;
    var variables;

    $.getJSON('/assets/json/surcharge.json', function(data) {
        surchargedata = data;
        console.log(surchargedata);
    });

    $.getJSON('/assets/json/ratio.json', function(data) {
        ratiodata = data;
        console.log(ratiodata);
    });

    $.getJSON('/assets/json/variables.json', function(data) {
        variables = data;
        console.log(variables);
    });

    $.getJSON('/assets/json/chartc3.json', function(data) {
        console.log("chartc3");
        console.log(surchargedata);
        console.log(ratiodata);
        console.log(variables);
        //Max value for y-axis
        scene = data;
        var maxs = 0;
        for (var j = 0; j < scene.length; j++) {
            var maxtemp = Math.max(scene[j].Marketable, scene[j]['Your Bid'], scene[j]['Total Requested Capacity']);
            maxs = Math.max(maxs, maxtemp);
        }

        //Chart
        var chart = c3.generate({
            bindto: '#chartc3',
            data: {
                json: scene,
                selection: {
                    enabled: false
                },
                keys: {
                    x: 'round',
                    value: ['Marketable', 'Total Requested Capacity', 'Your Bid'],
                },
                types: {
                    Marketable: 'area'
                },
                colors: {
                    Marketable: '#EBEBEB',
                        'Total Requested Capacity': '#272E80',
                        'Your Bid': '#00888A'
                }
            },
            point: {
                show: false
            },
            axis: {
                x: {
                    type: 'category',
                    label: {
                        text: 'Round Number',
                        position: 'outer-center',
                    }
                },
                y: {
                    min: 0,
                    padding: {
                        bottom: 0
                    },
                    tick: {
                        values: [
                            [0],
                            [maxs]
                        ],
                        format: function(d) {
                            return d3.format(',f')(d) + " " + variables.unit
                        }
                    }
                }
            },
            grid: {
                y: {
                    lines: [{
                        value: 10000000,
                        text: 'Value'
                    }]
                },
            },
            legend: {
                position: 'right'
            },
            regions: [{
                axis: 'x',
                start: 29,
                end: 34,
                class: 'regionX'
            }, ],
            tooltip: {
                contents: function(d, defaultTitleFormat, defaultValueFormat, color) {
                    var $$ = this,
                        config = $$.config,
                        CLASS = $$.CLASS,
                        titleFormat = config.tooltip_format_title || defaultTitleFormat,
                        nameFormat = config.tooltip_format_name || function(name) {
                            return name;
                        },
                        valueFormat = config.tooltip_format_value || defaultValueFormat,
                        text, i, title, value, name, bgcolor;

                    var count = 0;
                    for (i = 0; i < d.length; i++) {
                        if (!(d[i] && (d[i].value || d[i].value === 0))) {
                            continue;
                        }

                        if (!text) {
                            title = 'Round:' + scene[d[i].index].round + ', Ratio: ' + ratiodata[d[i].index].ratio + " %";
                            text = "<table class='" + CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
                        }

                        name = nameFormat(d[i].name);
                        value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
                        bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);

                        text += "<tr class='" + CLASS.tooltipName + "-" + d[i].id + "'>";
                        text += "<td class='name'><span style='background-color:" + bgcolor + "; border-radius: 5px;'></span>" + name + "</td>";
                        text += "<td class='value'>" + value + "</td>";
                        text += "</tr>";
                    } //for

                    text += "<tr class='" + CLASS.tooltipName + "-Surcharge" + "'>";
                    text += "<td id='footer'>" + "Surcharge: " + surchargedata[d[0].index].Surcharge + "</td>";
                    text += "<td id='footer'></td>";
                    text += "</tr></table>";

                    return text;
                }
            }

        }); //end chart
        //chart.select(['Marketable'], [31,32,33]);
    }); //end getjson
</script>

I know that getJson() is asynchronous and so I cant't be sure to load all of the data before firing the function for the chart (generate()). Besides I can't use the attribute async=false because it is deprecated.

What is the best solution to guarantee the right behavior?


Solution

  • You can store the promises returned from the $.getJSON calls in an array. You can then provide this array to $.when to execute the chart initialisation code once all the requests have completed. Try this:

    var promises = []
    promises.push($.getJSON('/assets/json/surcharge.json', function(data) { 
        surchargedata = data;
    }));
    promises.push($.getJSON('/assets/json/ratio.json', function(data) { 
        ratiodata = data;
    }));
    promises.push($.getJSON('/assets/json/variables.json', function(data){ 
        variables = data;
    }));
    
    $.when.apply(this, promises).done(function() {
        $.getJSON('/assets/json/chartc3.json', function(data) { 
            // setup chart here...
        });
    });
    

    Note however that it would be better to combine surcharge.json, ratio.json and variables.json to a single object which can be retrieved in one call.