Question

I am developing a Phonegap application which is dependent on loading multiple values from different text files into the application before it can run correctly. I thought that I was using callback functions correctly to address the asynchronous nature of JS, but the output from my program tells me that the code is being called in the reverse order...

In this run of the code, I have already set a waketime--which also sets a bedtime-- and saved both values to a text file.

The order of the calls should go like this:

Determine if the user is a new user or not... output is not necessary for other parts of application

Determine if the user has entered a waketime in the application before, if so read that time from the relevant text file, if not then use default wake & sleep time

Once we have the correct wake & sleep times, clear all previous notifications from notification queue

Once all previous notifications have been cleared, add new notifications for the current day within the time between waketime and bedtime... this is dependent on the bedtime and waketime already being determined

Concatenate all of the notification times generated into a string

Write this concatenated string to a text file

Relevant Javascript variable initialization:

    var defaultWakeTime = moment().hours(8).minutes(0).seconds(0);
    var defaultBedTime = moment().hours(23).minutes(0).seconds(0);

    //variables used to determine notification beginning and end range:
    var wakeTimeDate;
    var bedTimeDate;

in onDeviceReady():

function onDeviceReady() {
        console.log("WITHIN onDeviceReady()");

        document.addEventListener("pause", userHasGoneIdle , false);
        document.addEventListener("backbutton", onBackKeyDown, false);

        determineWhichLogin();

        determineWakeTimeBedTime(clearPriorNotifications(addNewNotifications));

        formNotificationString(writeToNotificationFile);
    }

The function codes:

function determineWakeTimeBedTime(callback){

        exists('wakeTimeBedTime.txt', function(value){
            if(value){

                console.log("wakeTimeBedTime.txt found");

                window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fs){
                    fs.root.getFile('wakeTimeBedTime.txt', null, function(fe){

                        fe.file(function(file){

                            var tempReader = new FileReader();
                            var content;
                            var lines = [];

                            tempReader.onloadend = function(evt){

                                content = evt.target.result;
                                lines = content.split(/\r\n|\r|\n/g);

                                console.log(lines[0] + '\n' + lines[1]);

                                wakeTimeDate = moment(lines[0]);
                                bedTimeDate = moment(lines[1]);
                            }

                            tempReader.readAsText(file);
                        }, onFSError);
                    }, onFSError);
                }, onFSError);
            }

            else{
                console.log("wakeTimeBedTime.txt NOT found");

                bedTimeDate = defaultBedTime;
                wakeTimeDate = defaultWakeTime;
            }

            //Once we've defined bedTimeDate & wakeTimeDate, it's time to create the notifications
            if (typeof callback === "function"){
                callback();
            }
        })
    }

function clearPriorNotifications(callback){
        window.plugin.notification.local.cancelAll();

        if (typeof callback === "function"){
            callback();
        }
    }

function addNewNotifications(){
        now = moment();

        setPartitionNotificationsForDay(now.toDate(), notificationCheckAndAdd);

        console.log('notificationCount: ' + notificationCount.toString());
    }

function formNotificationString(callback){
        var concatString='';

        for(var i = 0; i < notificationTimes.length; i++){
            concatString += notificationTimes[i].toDate();
            concatString += '\n';
        }

        if(typeof(callback) === 'function'){
            callback(concatString);
        }
    }

function writeToNotificationFile(passedString){

        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem){
            fileSystem.root.getFile("notification.txt", {create: true, exclusive: false}, function(fileEntry){
                //console.log("FILEENTRY.NAME: " + fileEntry);
                fileEntry.createWriter(function(writer){
                    notificationFileWriter = writer;
                    notificationFileWriter.seek(writer.length);
                    //console.log('within notificationfile createwriter()');
                    notificationFileWriter.write(passedString);
                    notificationFileWriter.onwriteend = function(evt){
                        //alert(evt);
                        //console.log("Done writing to notification.txt!");
                        console.log('notification written: ' + passedString);
                    }
                },onFSError);
            },onFSError);
        }, onFSError);
    }

Functions within addNewNotifications():

function setPartitionNotificationsForDay(passedDate, callback){
        var partitionAmt = (bedTimeDate.toDate() - wakeTimeDate.toDate()) / surveysInADay;
        partitionaAmt = Math.round(Math.abs(partitionAmt));

        //console.log("Partition amount: " + partitionAmt);

        didMissNotificationArr = [];
        notificationTimes = [];

        var oldMoment = moment(passedDate).hours(wakeTimeDate.hours()).minutes(wakeTimeDate.minutes());
        var newMoment = moment(oldMoment).add('milliseconds', partitionAmt);

        var randMoment;

        var bufferedOldMoment, bufferedNewMoment;

        for(var i = 0; i < surveysInADay; i++){
            bufferedOldMoment = oldMoment.clone().add('minutes', bufferMins);
            bufferedNewMoment = newMoment.clone().subtract('minutes', bufferMins)

            randMoment = randomMoment(bufferedOldMoment, bufferedNewMoment);

            notificationTimes.push(randMoment);
            didMissNotificationArr.push(true);

            oldMoment = newMoment;
            newMoment = moment(newMoment);
            newMoment.add('milliseconds', partitionAmt);
        }


        if (typeof(callback) === 'function'){
            callback();
        }

    }

//traverse notificationtimes, for all times that are greater than the current time,
//add notificaitons at those times
function notificationCheckAndAdd(callback){
        var tempMoment;
        var now = moment();

        //console.log("Within notificationCheckAndAdd()");
        notificationCount = 0;
        notificationsToday = 0;

        for(var i = 0; i < notificationTimes.length; i++){
            tempMoment = moment(notificationTimes[i]);

            if(tempMoment.isAfter(now)){
                notificationCount += 1;
                notificationsToday += 1;
                //console.log('Should add notification ' + notificationCount + ' at: ' + tempDate.toShortTimeString());
                addNotificationAtTime(tempMoment.toDate(), notificationCount);
                //break;
            }
        }

        //I dont really use the callback function within notificationCheckAndAdd
        if(typeof(callback) === 'function'){
            callback();
        }
    }

Output from debugging:

02-26 19:57:30.702: I/chromium(14683): [INFO:CONSOLE(902)] "WITHIN onDeviceReady()", source: file:///android_asset/www/index.html (902)
02-26 19:57:30.722: I/chromium(14683): [INFO:CONSOLE(1810)] "Uncaught TypeError: Cannot call method 'toDate' of undefined", source: file:///android_asset/www/index.html (1810)
02-26 19:57:30.812: I/chromium(14683): [INFO:CONSOLE(1966)] "
02-26 19:57:30.812: I/chromium(14683): oncancel() has been triggered id: 1", source: file:///android_asset/www/index.html (1966)

So what I can tell from this is that addNewNotifications() is being called before determineWakeTimeBedTime() can finish it's operation, since determineWakeTimeBedTime() assigns the variables wakeTimeDate and bedTimeDate, and the only way that they would be undefined is if addNewNotifications() started to execute before determineWakeTimeBedTime()...

How do I restructure my code so that I can get dependable, synchronous operations?

This is the tutorial that I have been using to better understand callback functions: JS Callback tutorial

Also cancel is being called after code was executed from addNewNotifications(), and log statements never occur within determineBedtimeWaketime()... so it seems like it's happening in reverse to me.

Was it helpful?

Solution

Working jsfiddle: http://jsfiddle.net/28Uuj/17/

Open your web inspector and refresh and the debugger; statements will break you into the debugger so you can see what is going on for each function call. Hover over the callback argument being passed into each function while debugging and you will see this puzzle unravel before your very eyes! :)

The code itself:

// What you actually want
// Wrap the function2 call inside an 
// anonymous function, so the interpeter doesn't try to evaluate it right away
console.log('What you actually want');
function1(function(){ function2(function3) });
// prints:
// What you actually want (index):22
// function1 (index):32
// function2 (index):39
// function3 


// The way you have it now 
// The interpreter thinks you want to 
// evaluate function2(function3) and then pass the RETURN value
// of that into function1
console.log('The way you have it now');
function1(function2(function3));
// prints:
// The way you have it now (index):26
// function2 (index):39
// function3 (index):46
// function1 (index):32
// Uncaught TypeError: undefined is not a function (index):3


function function1(callback) 
{
    debugger;
    console.log('function1');
    callback();
}

function function2(callback) 
{
    debugger;
    console.log('function2');
    callback();
}

function function3() 
{
    debugger;
    console.log('function3');
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top