Question

I have to execute a query , get id and execute 3 more query with a for loop. In this function I concatenate the variable . But the values within the nested query does not get appended. Please help me find a better solution for this problem .

db.transaction(function (tx) {
    tx.executeSql('SELECT * FROM contacts ', [],
        function (tx, results) {
            for (var i = 0; i < results.rows.length; i++) {
                var row = results.rows.item(i);
                var ds = "";
                ds += "";
                ds += "BEGIN:VCARD\n"
                ds += "VERSION:2.1\n"
                ds += "N:" + row.last_name + ";" + row.first_name + ";" + row.middle_name + ";" + row.prefix + "\n"
                ds += "FN:" + row.prefix + " " + row.first_name + " " + row.middle_name + " " + row.last_name + "\n"
                ds += "ORG:" + row.company + "\n"
                ds += "TITLE:" + row.title + "\n"

                // append phone numbers
                tx.executeSql("Select * from contact_details where contact_id='" + row._id + "' and data_type='1' ", [],
                    function (tx, resu) {
                        if (resu.length != 0) {

                            for (b = 0; b < resu.rows.length; b++) {
                                var myrow = resu.rows.item(b);

                                if (myrow.contact_type == "Home LandLine")
                                    ds += "TEL;HOME;VOICE:" + myrow.contact_data + "\n"
                                if (myrow.contact_type == "Home Mobile")
                                    ds += "TEL;HOME;CELL:" + myrow.contact_data + "\n"
                                if (myrow.contact_type == "Home Fax")
                                    ds += "TEL;HOME;FAX:" + myrow.contact_data + "\n"

                                if (myrow.contact_type == "Work LandLine")
                                    ds += "TEL;WORK;VOICE:" + myrow.contact_data + "\n"
                                if (myrow.contact_type == "Work Mobile")
                                    ds += "TEL;WORK;CELL:" + myrow.contact_data + "\n"
                                if (myrow.contact_type == "Work Fax")
                                    ds += "TEL;WORK;FAX:" + myrow.contact_data + "\n"
                            }

                        }
                    })


                //ds+=$("#hidexport").text();
                // append emails
                tx.executeSql("Select * from contact_details where contact_id='" + row._id + "' and data_type='2' ", [],
                    function (tx, resuy) {
                        for (y = 0; y < resuy.rows.length; y++) {
                            var myrowy = resuy.rows.item(y);
                            ds += "EMAIL;INTERNET:" + myrowy.contact_data + "\n"
                        }
                    });

                // append websites
                tx.executeSql("Select * from contact_details where contact_id='" + row._id + "' and data_type='3' ", [],
                    function (tx, resuz) {
                        for (z = 0; z < resuz.rows.length; z++) {
                            var myrowz = resuz.rows.item(z);
                            ds += "URL:" + myrowz.contact_data + "\n"
                        }
                    });

                // append address
                ds += "ADR;WORK:" + row.company_add_1 + ";" + row.company_add_2 + ";" + row.company_city + ";" + row.company_region + ";" + row.company_state
                ds += ";" + row.company_zip + ";" + row.company_country + "\n"
                ds += "LABEL;WORK;ENCODING=QUOTED-PRINTABLE:" + row.company_add_1 + ";" + row.company_add_2 + ";" + row.company_city + ";"
                ds += row.company_region + ";" + row.company_state + ";" + row.company_zip + "=0D=0" + row.company_country + "\n"

                console.log(ds);

            }
        });
});
}
Was it helpful?

Solution

Your Sql queries are asynchronous. That means that they complete some time LATER, long after your function has exited. If you want to run three asynchronous operations one after the other, then you can't just code them sequentially like you've done. All that does is start them all at once and then they all finish sometime later.

Further, you can't return the results from your function because your function finishes right away, but the asynchronous operations finish some time later.

The conceptually simplest way to fix this (not super elegant, but conceptually simple) is to start the first operation and in the completion function for the first operation, you collect the results from it and then in that completion function, you start the second operation. It will then have it's own completion function and in that completion function you start the third operation and in it's completion function, you will now have collected all the results. You can then (from inside the third nested completion function), take your results and call some external function and pass it the results. Only from within that final completion function do you have the results and you can use them.

Here's what this looks like conceptually:

tx.executeSql("Select ...", function(tx, resuz) {
    ds += ...;
    tx.executeSql("Select ...", function(tx, resuz) {
        ds += ...;
        tx.executeSql("Select ...", function(tx, resuz) {
            ds += ...;
            console.log(ds);
            processResult(ds);
        });
    });
});

There are many other ways to serialize multiple asynchronous operations that are a bit more elegant. You can use promises, you can use task queues, etc...

If the queries can be run in parallel, you can run them faster by launching all three queries at once, collecting results and then processing the results when all of them are done executing. Conceptually that looks like this:

var results = [null, null, null];
var resultCntr = results.length;

function checkResults() {
    if (resultCntr === 0) {
        // all results are in now
        ds += results[0] + results[1] + results[2];
        console.log(ds);
        processResult(ds);
    }
}

tx.executeSql("Select ...", function(tx, resuz) {
    results[0] = ...;
    --resultCntr;
    checkResults();
});
tx.executeSql("Select ...", function(tx, resuz) {
    results[1] = ...;
    --resultCntr;
    checkResults();
});
tx.executeSql("Select ...", function(tx, resuz) {
    results[2] = ...;
    --resultCntr;
    checkResults();
});

If your tasks are similar, they can probably be factored into some common code rather than simple copy/paste three times.


If you want to do the equivalent of a for loop on a bunch of asynchronous operations, then you can't use a for loop. Instead, you have to do something conceptually like this where you do the next iteration of your loop from the completion handler of the previous iteration:

var rowCntr = 0;
var ds = "";
function processRow() {
    var row = results.rows.item(rowCntr);
    tx.executeSql("Select ...", function(tx, resuz) {
         ds += ...
         ++rowCntr;
         if (rowCntr < results.rows.length) {
             processRow();
         } else {
             // all rows done now
             // do whatever you want to do when you've collected the data for all the rows
         }
    });
}
// process the first row
processRow();    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top