Search code examples
javascriptandroidtitaniumappcelerator

How to wait for a function to complete before moving to next in loop in Android using Appcelerator


So I have an app which preloads graphics from an API.

I firstly grab all of the data I need from my onboard DB, loop through it and send the user off to a function to download the image, resize it etc.

on iOS this seems to work great, however on Android it keeps falling over.

I'm looking for a more elegant way of doing this, that won't crash the app.

Is there a way I can wait for the getMarker() function to complete before my loop (from the getMapMarkers() function) fires another request to it?

Here is my code snippet for that part of my app. The function getMarker() does the processing of the image and resizing.

function getMarker(url, filename) {

    var mapMarker = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'map_marker_icons', filename);

    // now we need to download the map marker and save it into our device 
    var getMarker = Titanium.Network.createHTTPClient({
        timeout: 30000
    });

    getMarker.onload = function() {

        // if the file loads, then write to the filesystem
        if (getMarker.status == 200) {

                var image = this.responseData;
                // resize to 75 pixel marker
                var resizedImage = image.imageAsResized(75, 75);
                mapMarker.write(resizedImage);

                //I ALWAYS NULL ANY PROXIES CREATED SO THAT IT CAN BE RELEASED
                image = null;
                resizedImage = null;
                mapMarker = null;


        } else {
            Ti.API.info("Image not loaded");
        }


        //getMarker = null;


    };

    getMarker.onerror = function(e) {
        Ti.API.info('XHR Error ' + e.error);
        //alert('markers data error');
    };

    getMarker.ondatastream = function(e) {


        if (e.progress == 1) {
            Ti.API.info(filename + ' Download Complete');

        }
    };

    // open the client
    getMarker.open('GET', url);

    // send the data
    getMarker.send();

}

function getMapMarkers() {
    // get the species list back
    var db = Ti.Database.open('myDB');
    var getSpeciesImages = db.execute('SELECT speciesiconfilename, speciesmapiconurl FROM species where speciesiconfilename <> ""');

    // YOU ONLY NEED TO DO THIS ONCE SO DO IT OUTSIDE THE LOOP
    var imgDir = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'map_marker_icons');

    // if the directory doesn't exist, then we need to create it
    if (!imgDir.exists()) {
        // If the directory doesn't exist, make it
        imgDir.createDirectory();
    };

    // start the loop
    while (getSpeciesImages.isValidRow()) {
        var filename = getSpeciesImages.fieldByName('speciesiconfilename');
        var url = getSpeciesImages.fieldByName('speciesmapiconurl');

        getMarker(url, filename);

        getSpeciesImages.next();

    } // end the loop

    getSpeciesImages.close();


    // close the database
    db.close();

    // get the exhibit markers next
    getExhibitMapMarkers();
};

Anyone able to help? It's driving me crazy!

Simon


Solution

  • It's more js question than titanium. I prefer to use Q library (https://github.com/kriskowal/q) for such things.

    function getMarker(url, filename) {
        return function() {
            var defered = Q.defer();
    
            ...
            var xhr = Titanium.Network.createHTTPClient({
                timeout: 30000
            });
    
            xhr.onload = function() {
                ...
                defered.resolve();
            };
    
            xhr.onerror = function(e) {
                ...
                defered.resolve(); // or defered.reject() if you want stop after first error
            }
            ...
            xhr.open('GET', url);
            xhr.send();
    
            return defered.promise;
        };
    }
    
    function getMapMarkers() {
        ...
        var imageRequests = [];
    
        while(getSpeciesImages.isValidRow()) {
            ...
            imageRequests.push(getMarker(url, filename));
            getSpeciesImages.next();
        }
    
        imageRequests.reduce(Q.when, Q(true))
        .then(function() {
            Ti.API.info('all loaded');
        });
        ...
    };
    

    And don't name http client variable same as getMarker() function!