I have created a function that will return the string "path" from my DB.
function getAudio(mid, cb) {
//mid is an array containing the id to some multimedia files.
for(i=0; i < mid.length; i++) {
db.transaction(function(tx) {
tx.executeSql('SELECT * FROM Multimedia WHERE Mid=' + mid[i] + ' AND Type=' + 2, [], function(tx, results) {
//type=2 is audio. (type=1 is picture file etc) There is only one audiofile in the array.
if(results.rows.length > 0) {
path = results.rows.item(0).Path;
cb(path);
}
}, errorCB);
}, errorCBQuery);
}//end for
}//end getAudio()
When I remove the for-loop, the query is successfull, when the for-loops is there, errorCBQuery or errorCB is called.
Any Ideas on how to fix this? Thanks :)
1 Answer 1
This is the classic closure problem. transaction is an asynchronous call, which means your loop completes before the function you pass in is triggered. The function has an enduring reference to the i variable, not a copy of it as of when you called transaction. So each of those functions (you're generating one on each loop) is seeing i == mid.length and thus mid[i] is undefined and your SQL gets messed up.
What you need to do is have the callbacks close over another variable that won't change when the loop proceeds. The usual way to do that is to use a factory function:
function getAudio(mid, cb) {
//mid is an array containing the id to some multimedia files.
for(i=0; i < mid.length; i++) {
db.transaction(makeTx(mid[i]), errorCBQuery);
}//end for
function makeTx(val) {
return function(tx) {
tx.executeSql('SELECT * FROM Multimedia WHERE Mid=' + val + ' AND Type=' + 2, [], function(tx, results) {
//type=2 is audio. (type=1 is picture file etc) There is only one audiofile in the array.
if(results.rows.length > 0) {
path = results.rows.item(0).Path;
cb(path);
}
}, errorCB);
};
}
}//end getAudio()
There, I'm passing mid[i] into the makeTx function, which returns the function that will get passed into transaction. The function we return closes over the val argument for the call to makeTx that created it, which doesn't change.
That's a minimal rewrite of the code; you can probably take it further. For example, see missingno's comment on the question re parameterized statements.
Side note: It doesn't look like you declare i or path anywhere. If that's really true, you're falling prey to The Horror of Implicit Globals . Recommend declaring them.
tx.executeSql('SELECT * FROM Multimedia WHERE Mid=? AND Type=?', [mid[i], 2].