-1

I am trying to pull back several sets of statistical data and display progress messages once each set of data has been loaded onto the DOM.

I am trying to use multiple deferreds along with the setTimeout function to manage the response to the user.

I begin calling a function to display my loading message. I then use AJAX to retrieve the data. The success function then loops through the data sets and:

  1. Defines the current data set in a stat variable
  2. Invokes the loadDomElement function (see explanation below) which returns a promise
  3. Pushes the returned promise into my promises array
  4. Uses the .apply function to pass the promises array and when all promises have been done, it hides the loading block to reveal all of the elemens that have been loaded in the background.
$(function() {

        displayMyWaiter('Fetching month end statistics for all pitches....');

        $.ajax ( {
            url: '../../ajax/stats.php',
            method: 'GET',
            data: {mode: 'all statistics for ent',  statEntity: 'pitch', statType: 'total', statFilter: 'distinct'},
            dataType: 'json',
            type: 'POST',
            success: function(statGroup){
                console.log(statGroup);

                var promises = [];

                for (var i in statGroup) {

                    var stat = statGroup[i];
                    console.log(stat);
                    var def = $.Deferred();

                    $.when( loadDomElement(stat) ).done(function() {
                        console.log( "hello" );
                        def.resolve();
                        promises.push(def);
                    });

                };

                $.when.apply($, promises).done(function() {
                    hideMyWaiter("slide");
                });

            }       
        });        
    });

The loadDomElement function has the current stat passed to it as an argument and returns a promise. It essentially appends a html block to my page. It then invokes the loadStat function which also returns a promise - which resolves the deferred in the loadDomElement function. The code looks like this:

function loadDomElement(stat) {
        var dfd = $.Deferred();
        var statPanelID = 'stat_block_' + stat.code;
        $('#stats').append( '<div id="' + statPanelID + '"></div>' );

        $.when( loadStat(statPanelID, stat) ).done(function() {
            dfd.resolve();
        });

        return dfd.promise()
    }

The loadStat function begins by creating an outerDef deferred variable. It then invokes my function to update the message in the Loading block. This returns a promise:

function updateMyWaiter(str) {

        var text = "<li class='waiter-item'>" + str + "</li>"
        var dfd = $.Deferred();
        setTimeout(function(){ 
            $('.busyText').append(text);
            dfd.resolve();
        }, 3000);
        return dfd.promise();
    }

When the promise is returned it then invokes my panel function (which also returns a promise) and uses my custom plugin to load teh statistical data onto the page (behind the Loading message). The function looks like this:

function loadStat(statPanelID, stat) {

        console.log("Now create stats for " + stat.code + ": " + stat.desc + " and place it in this block: " + statPanelID);
        var outerDef = $.Deferred();

        $.when( updateMyWaiter("Loading stats for " + stat.desc + "( code: " + stat.code + ")") ).done(function() {

            var panel = function() {

                var d = $.Deferred();
                var panelTitle = (stat.desc === null ? 'Pitch ' + stat.code : stat.desc);
                var statPluginID = 'stat_' + stat.code;

                $("#" + statPanelID).panIt({
                    allowScrolling: false,
                    header: {
                        title: panelTitle
                    },
                    body: {
                        content: '<div id="' + statPluginID + '">Position stat plugin for ' + stat.desc + ' here.</div>',
                        height:  '700'                                
                    },
                    onLoaded: function() {
                        $.when ( $('#' + statPluginID).stats({ // dom ID for stat plugin
                            statEntity: 'pitch',
                            statEntityID: stat.code,
                            statType: 'total',
                            statPeriod: null,
                            chartType: 'line',
                            chartTitle: 'Pitch stats for: ' + stat.desc,
                            activeTab: 'chart',
                            clickOptions: {
                                enabled: false
                            },
                            chartHeight: 700,
                            table: {
                                headers: [
                                    { name: 'Month', data: 'month' },
                                    { name: 'BF', data: 'bf' },
                                    { name: 'New', data: 'new_mem' },
                                    { name: 'Trn In', data: 'tra_in' },
                                    { name: 'Trn Out', data: 'tra_out' },
                                    { name: 'Lapsed', data: 'lapsed' },
                                    { name: 'CF', data:   'cf' },
                                    { name: 'Balance', data: 'balance'}
                                ],
                                hasSelect:  false,
                                search: false
                            }
                        })).done (function(result) {
                            d.resolve ('Pitch stats: ' + result);
                        });
                    }
                });
                return d.promise();

            };

            $.when ( panel() ).done (function(result) {
                console.log(result);
                outerDef.resolve(result); 
            });

        });

        return outerDef.promise();
    }

I essentially want the application to begin with my intiail message: 'Fetching month end statistics for all pitches....' and then for each statistic it should update the loading message with anotehr line that says which statistic is now being loaded. Once all statistics have been loaded, the loading message should be hidden.

But what happens is that I get the intial message which is only displayed for a second before it is hidden. The applicaiton then proceeds to load the data to the DOM.

Lee A
  • 45
  • 1
  • 9
  • 1
    you need to use promise objects https://javascript.info/promise-basics – Hogan Mar 28 '19 at 14:37
  • @Taplar whoops I got delegated mixed up with deferred, need glasses – zer00ne Mar 28 '19 at 15:52
  • Thanks @Hogan. Just to clarify, are you saying this can't be done using JQuery deferred or simply that promises are easier or better to use? Thanks – Lee A Mar 28 '19 at 16:45

1 Answers1

0

I found the answer in another log: click here

I tweaked my code as follows:

$(function() {

        displayMyWaiter('Fetching month end statistics for all pitches....');

        $.ajax ( {
            url: '../../ajax/stats.php',
            method: 'GET',
            data: {mode: 'all statistics for ent',  statEntity: 'pitch', statType: 'total', statFilter: 'distinct'},
            dataType: 'json',
            type: 'POST',
            success: function(statGroup){
                handlePitchData(statGroup, 0); 
            }        
        });        

    });

function handlePitchData(statGroup, i) {

        if(i<statGroup.length){
            var stat = statGroup[i];
            console.log(statGroup[i]);
            $.when( loadDomElement(stat) ).done(function() {
                handlePitchData(statGroup, i+1);
            });    
        } else {
            hideMyWaiter("slide");
        }

    }



    function loadDomElement(stat) {
        var dfd = $.Deferred();
        var statPanelID = 'stat_block_' + stat.code;
        $('#stats').append( '<div id="' + statPanelID + '"></div>' );

        $.when( updateMyWaiter("Loading stats for " + stat.desc), loadStat(statPanelID, stat) ).done(function() {
            dfd.resolve();;
        });

        return dfd.promise();
    }

function loadStat(statPanelID, stat) {

        console.log("Now create stats for " + stat.code + ": " + stat.desc + " and place it in this block: " + statPanelID);
        var outerDef = $.Deferred();

        var panel = function() {

            var d = $.Deferred();
            var panelTitle = (stat.desc === null ? 'Pitch ' + stat.code : stat.desc);
            var statPluginID = 'stat_' + stat.code;

            $("#" + statPanelID).panIt({
                allowScrolling: false,
                header: {
                    title: panelTitle
                },
                body: {
                    content: '<div id="' + statPluginID + '">Position stat plugin for ' + stat.desc + ' here.</div>',
                    height:  '700'                                
                },
                onLoaded: function() {
                    $.when ( $('#' + statPluginID).stats({ // dom ID for stat plugin
                        statEntity: 'pitch',
                        statEntityID: stat.code,
                        statType: 'total',
                        statPeriod: null,
                        chartType: 'line',
                        chartTitle: 'Pitch stats for: ' + stat.desc,
                        activeTab: 'chart',
                        clickOptions: {
                            enabled: false
                        },
                        chartHeight: 700,
                        table: {
                            headers: [
                                { name: 'Month', data: 'month' },
                                { name: 'BF', data: 'bf' },
                                { name: 'New', data: 'new_mem' },
                                { name: 'Trn In', data: 'tra_in' },
                                { name: 'Trn Out', data: 'tra_out' },
                                { name: 'Lapsed', data: 'lapsed' },
                                { name: 'CF', data:   'cf' },
                                { name: 'Balance', data: 'balance'}
                            ],
                            hasSelect:  false,
                            search: false
                        }
                    })).done (function(result) {
                        d.resolve ('Pitch stats: ' + result);
                    });
                }
            });
            return d.promise();

        };

        $.when ( panel() ).done (function(result) {
            console.log(result);
            outerDef.resolve(result); 
        });

        return outerDef.promise();
    }


In short, I used the recursive method.

Lee A
  • 45
  • 1
  • 9