2
\$\begingroup\$

I'm developing a mobile app with Cordova, jQuery and jQuery mobile. To synchronise the data saved on a device I make AJAX calls to a server (I'm sending and requesting data). The data is saved in a database on the device with this SQLite plugin for cordova. The server-side connection is handled by a php file.

Everytime the app is started a GET call is made to check for updates and everytime a new note is made the data is send with a PUSH call.

function onDeviceReady(){
    //global variables, bad practice I know
    user = 'user';
    pass = 'pass';
    deviceID = device.uuid;
    console.log(deviceID);
    db = window.sqlitePlugin.openDatabase({name: 'mood.db', location: 'default'},function(){
        db.transaction(function(tx){
            tx.executeSql('CREATE TABLE IF NOT EXISTS update_table (uid integer primary key autoincrement, timestamp)',[],function(){},function(e){
                console.log(e.message);
            });
            tx.executeSql('CREATE TABLE IF NOT EXISTS mood_table (uid integer primary key autoincrement, uuid, id, smileyid, textdata, activity, created, last_change, deleted)',[],function(){},function(e){
                console.log(e.message);
            });
            tx.executeSql('SELECT * FROM update_table',[],function(tx, res){
                if(res.rows.length === 0){
                    tx.executeSql('INSERT INTO update_table (timestamp) VALUES (?)',[0],function(){},function(e){
                        console.log(e.message);
                    });
                }
            },function(e){
                console.log(e.message);
            });
            checkForUpdates();
        },function(e){
            console.log(e.message);
        });
    },function(e){
        console.log(JSON.stringify(e));
    });
}

function make_base_auth(username, password) {
    var tok = username + ':' + password;
    var hash = btoa(tok);
    return 'Basic ' + hash;
}

function checkForUpdates(){
    console.log('checkForUpdates');
    //internet connection check
    if(navigator.connection.type != Connection.NONE) {
        db.executeSql('SELECT * FROM update_table WHERE uid = 1',[],function(res){
            var data,
                new_tsmp,
                tsmp = (typeof res.rows.item(0).timestamp === undefined) ? '0' : res.rows.item(0).timestamp;
            console.log(tsmp);
            $.when(
                $.ajax({
                    url: 'http://mydomain.de/my.php',
                    crossDomain: true,
                    timeout: 30000,
                    method: 'GET',
                    dataType: 'text',
                    data: {'command': 'check_update',
                            'unix_time': tsmp,
                            'uuid': deviceID},
                    xhrFields: {
                        withCredentials: true
                    },
                    beforeSend: function(xhr){
                        xhr.setRequestHeader('Authorization', make_base_auth(user, pass));
                    },
                    success: function(textdata, status, jqXHR){
                        data = $.parseJSON(textdata);
                        //check for updates
                        console.log(JSON.stringify(data));
                        console.log(status);
                    },
                    error: ajaxErrorCb
                })
            ).done(function(){
                switch(data.case){
                    case 'update':
                        //update
                        new_tsmp = data.timestamp;
                        $.makeArray(data.notes_id).forEach(function(ele, i){
                            if(data.deleted[i] === '0'){
                                getData(ele);
                            } else {
                                removeData(data.uuid[i], data.id[i]);
                            }
                        });
                        break;
                    case 'stay':
                        //???
                        new_tsmp = tsmp;
                        populate();
                        break;
                    default:
                        console.log('something is not right');
                }
                updateTimestamp(new_tsmp);
            });
        },function(e){
            console.log(e.message);
        });
    } else {
        //popup??
        alert('Die Daten können nicht mit dem Server synchronisiert werden, da keine Internetverbindung vorhanden ist.')
        populate();
    }
}

function removeData(uuid, id){
    db.executeSql('DELETE FROM mood_table WHERE uuid = ? AND id = ?',[uuid, id],function(){
        console.log('removed: '+uuid+', '+id);
    },function(e){
        console.log(e.message);
    })
}

function updateTimestamp(tsmp){
    db.executeSql('UPDATE update_table SET timestamp = ? WHERE uid = 1',[tsmp],function(){},function(e){
        console.log(e.message);
    }); 
}

/**
 *Downloads data from the server and inserts it into the local database.
 *@param {integer} id - The id of the row that will be downloaded
 */
function getData(id){
    $.ajax({
        url: 'http://mydomain.de/my.php',
        crossDomain: true,
        timeout: 30000,
        method: 'GET',
        dataType: 'json',
        data: {'command': 'get_update_data',
                'id': id},
        xhrFields: {
            withCredentials: true
        },
        beforeSend: function(xhr){
            xhr.setRequestHeader('Authorization', make_base_auth(user, pass));
        },
        success: function(data,status,jqXHR){
            //insert updated data
            console.log(JSON.stringify(data));
            var activityArr = new Array(),
                smileyimg = getSmileyImg(data.smileyid, data.id),
                activityValues = data.activity.split(","),
                date = new Date(parseInt(data.created, 10)),
                dd = date.getDate(),
                mm = date.getMonth()+1, //January is 0!
                yyyy = date.getFullYear(),
                hh = date.getHours(),
                mins = date.getMinutes();
            if(dd<10) {
                dd='0'+dd;
            } 
            if(mm<10) {
                mm='0'+mm;
            } 
             if(mins<10) {
                mins='0'+mins;
            } 
            var created = hh+':'+mins+' '+dd+'.'+mm+'.'+yyyy,
            activityValues.forEach(function(ele, i){
                if(ele == 1){
                    activityArr.push(getActivity(i));
                }
            });
            //check uuid & id
            db.transaction(function(tx){
                tx.executeSql('SELECT * FROM mood_table WHERE uuid = ? AND id = ?',[data.uuid, data.id],function(tx, res){
                    if(res.rows.length != 0){
                        tx.executeSql('UPDATE mood_table SET smileyid = ?, textdata = ?, activity = ?, last_change = ?, deleted = ? WHERE uuid = ? AND id = ?',[data.smileyid, data.textdata, data.activity, data.last_change, data.deleted, data.uuid, data.id],function(){
                            console.log('insert success');
                        },function(e){
                            console.log(e.message);
                        });
                    } else {
                        tx.executeSql('INSERT INTO mood_table (uuid, id, smileyid, textdata, activity, created, last_change, deleted) VALUES (?,?,?,?,?,?,?,?)',[data.uuid, data.id, data.smileyid, data.textdata, data.activity, created, data.last_change, data.deleted],function(){
                            console.log('insert succes');
                        },function(e){
                            console.log(e.message);
                        });
                    }
                },function(e){
                    console.log(e.message);
                });
                //populate?
                $('#mytext').prepend('<div class="mytextarea" id="'+data.id+'"></div>');
                if(data.uuid == deviceID){
                    $('#'+data.id).append('<a href="#popupDialog" data-rel="popup" data-position-to="window" class="ui-btn ui-shadow ui-corner-all ui-nodisc-icon ui-alt-icon ui-icon-bars ui-btn-icon-notext ui-btn-inline delete-button" onclick="openPopup('+data.id+');"></a>');
                } else {
                    $('#'+data.id).append('</br>');
                }
                $('#'+data.id).append(smileyimg+'<p id="'+data.id+'-text" style="margin-top: 10px;">'+data.textdata+'</p><p><div id="taetigkeiten_'+data.id+'"></div>'+created+'</p>');
                $('#taetigkeiten_'+data.id).text('Tätigkeiten: ');
                $('#taetigkeiten_'+data.id).append(activityArr.join(', '));
                graphMalen(smileyArr);
            });
        },
        error: ajaxErrorCb
    });
}

/**
 *Uploads data to the server.
 *@param {integer} id - ID of the div
 *@param {integer} smileyid - ID of the smiley
 *@param {string} text - Descriptiontext
 *@param {integer} activity - ID of the activity
 *@param {string} date - Date and time of the creatino
 */
function insertData(id, smileyid, text, activity){
    var insertObj = {
        'command': 'insert_data',
        'uuid': deviceID,
        'id': id,
        'smileyid': smileyid,
        'text': text,
        'activity': activity.join(',')
    };
    $.ajax({
        url: 'http://mydomain.de/my.php',
        crossDomain: true,
        timeout: 30000,
        method: 'POST',
        dataType: 'text',
        data: insertObj,
        xhrFields: {
            withCredentials: true
        },
        beforeSend : function(xhr){
            xhr.setRequestHeader('Authorization', make_base_auth(user, pass));
        },
        success: function(data,status){
            console.log(JSON.stringify(data));
            console.log(status);
        },
        error: ajaxErrorCb
    });
}

function ajaxErrorCb(obj, text, e){
    console.log(JSON.stringify(obj));
    console.log(text);
}

In the checkForUpdates function I'm using $.when(...).done(...); because I previously had troubles appending the HTML in a for/foreach loop which is done in the success part of the getData ajax call.


populate() appends the HTML with local saved data.
graphMalen() creates a graph with CSS.

The data I get in all the ajax calls look like this:

checkForUpdates:

{'case': string,
'notes_id': integer,
'uuid': string,
'id': integer,
'deleted': integer,
'timestamp': integer}

getData:

{'uuid': string,
'id': integer,
'smileyid': integer,
'textdata': string,
'activity': string,
'timedate': integer,
'created': integer,
'last_change': integer,
'deleted': integer}

Are there any improvements I could make the the code? Specifically the ajax calls, I always have a hard time because they are asynchronous and I don't want to set async to false because of performance.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

Two observations :

1. Transactions

Calling a function from within a db.transaction() callback does not in itself cause that function to be part of the transaction.

It seems likely that everything in checkForUpdates() and its sub-functions should be subject to the transaction defined in onDeviceReady().

If that's what you want (and you probably do), then you need to pass tx and use it in called functions rather that defining separate transactions.

2. Asynchronous flow control

There's little asynchronous flow control in the code. Much of it is written as if it was synchronous.

For example, both getData() and removeData() perform asycn operations but neither informs its caller of completion. Therefore there's no hope in the caller (or the caller's caller, etc) to schedule things to happen when those async activities have comepleted.

If sequencing matters (and it probably does) then you need to be working with promises.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.