1

BACKGROUND

I have made a small site to run a tipping competition. I am using chartjs to draw garphs. I have made a function (createCjsDonutDataWinnerPickData) to format the data for the graph in to a format suitable for chartjs.

THE PROBLEM

For some reason the function (createCjsDonutDataWinnerPickData) is returning undefined. I think it is due to the "return" returning before the function has finished processing.

I am calling the function in two places. The first is a test before the page is rendered and the second time is when rendering the page.

Extract from page rendering function

Point.findOne({user:req.user.id, competition:comp.id, fixture:fixture.id}).exec(function (err, points){
        if (err) {console.log('ERR: fixtures pick page on COMP lookup')}
        else {
            console.log('TEST FUNCTION');
            console.log(createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams)));
            res.render('fixturePick.ejs', {
                user : req.user, // get the user out of session and pass to template
                fixture: fixture,
                pick: pick,
                teams: createIdLookup(teams),
                draw: draw,
                round: round,
                competition: comp,
                points: points,
                gWinnerPickGraph: JSON.stringify( createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams)) ),                                   
                successMsg: req.flash('successMsg'),
                dangerMsg: req.flash('dangerMsg'),
                warningMsg: req.flash('warningMsg')
            });
        }
    });

The function createCjsDonutDataWinnerPickData

function createCjsDonutDataWinnerPickData(fixtureID,competitionID,teamLookup){
    var Statistic = require('../app/models/statistic');
    var Fixture = require('../app/models/fixture');
    var async = require('async');

    try {
        async.waterfall([
            function(cb_ReturnData){
                var chartData =[];
                Statistic.findOne({fixture:fixtureID, competition:competitionID,type:'winnerPickNumber'}).populate('fixture').exec(function (err,statData){
                    if (err) {console.log('ERROR in preparing data for graph');throw (err)}
                    else {

                        async.each(statData.data, function(dataPoint,cb_PrepData){
                            var sliceData = {};
                            if (statData.fixture.homeTeam._id == dataPoint.teamID){
                                //console.log('Found data for home team')
                                sliceData = {value: dataPoint.number, color:"rgba(151,187,205,0.5)", highlight: "rgba(151,187,205,0.75)", label:teamLookup[dataPoint.teamID].name };
                            }
                            else
                            {
                                //console.log('Found data for away team')
                                sliceData = {value: dataPoint.number, color:"rgba(220,220,220,0.5)", highlight: "rgba(220,220,220,0.75)", label:teamLookup[dataPoint.teamID].name };
                            }
                            //console.log('Pusihgin slice data to data')
                            chartData.push(sliceData);
                            cb_PrepData();
                        }, function(err){
                            if (err) {
                                console.log('ERROR in creating data for WinnerPickGraph');
                                cb_ReturnData(err);
                            }
                            else {
                                //console.log('CHART DATA IN INNER ASYNC');
                                //console.log(chartData);
                                cb_ReturnData(null, chartData);
                            }
                        });

                    }
                });


            }
            ],function(err,chartData){
                if (err) {console.log('ERROR in preparing return data')}
                else {
                    console.log('HERE IS THE FINAL RETURN');
                    console.log(chartData);
                    return (chartData);
                }
            });
     }
    catch (err){
        //probably end up here because there is no points History Data
        console.log('threw erro returning undefined');
        return undefined;
    }
}

Console output

GET /fixturePick?competition=542a5ffa736e3e35532f2d24&fixture=542c9ae12367c9209a739150 302 5.532 ms - 58
PATH: /fixturePick?competition=542a5ffa736e3e35532f2d24&fixture=542c9ae12367c9209a739150
GET / 200 11.291 ms - 1515
GET /images/grassBackground.jpg 200 5.533 ms - 145369
POST /login 302 459.431 ms - 228
TEST FUNCTION
undefined
GET /fixturePick?competition=542a5ffa736e3e35532f2d24&fixture=542c9ae12367c9209a739150 200 1609.303 ms - 4984
GET /images/team/logo/sm/53fc6399b918a6b661d423b4.png 200 2.720 ms - 15747
HERE IS THE FINAL RETURN
[ { value: 1,
    color: 'rgba(220,220,220,0.5)',
    highlight: 'rgba(220,220,220,0.75)',
    label: 'Melbourne Victory' },
  { value: 2,
    color: 'rgba(220,220,220,0.5)',
    highlight: 'rgba(220,220,220,0.75)',
    label: 'Western Sydney Wanderers' } ]
HERE IS THE FINAL RETURN
[ { value: 1,
    color: 'rgba(220,220,220,0.5)',
    highlight: 'rgba(220,220,220,0.75)',
    label: 'Melbourne Victory' },
  { value: 2,
    color: 'rgba(220,220,220,0.5)',
    highlight: 'rgba(220,220,220,0.75)',
    label: 'Western Sydney Wanderers' } ]
GET /images/team/logo/sm/53fc6399b918a6b661d423b5.png 200 2.368 ms - 13144
GET /images/none.png 200 1.857 ms - 195

I may have overcomplicated this with async.foreach and async.waterfall but I was running out of ideas as to why the function keeps returning undefined.

Leslie
  • 19
  • 2

1 Answers1

0

createCjsDonutDataWinnerPickData will return undefined always because it's asynchronous. To get a handle on results, you should either pass it a callback or return a promise.

Here are some examples of the approaches:

// callback
createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams), function(err, data) {
    // do something with err and data now
});

// promise (es6-promise)
createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams)).then(function(data) {
    // got some data, do something
}).catch(function(err) {
    // deal with the error
});

Latter approach is possible with es6-promise. bluebird works well too.

Juho Vepsäläinen
  • 26,573
  • 12
  • 79
  • 105