Synchronous WebSQL Transaction with another transaction inside - javascript

I know that I cannot retrieve the value of transaction Synchronously. I Read that an alternative is to Callback the function. But I can't manage to do it.. here's a snippet where I inserted the second transaction inside a function.
for(...){
db.transaction(function(tx) {
tx.executeSql('SELECT ...', function(tx, results) {
...
...
weight = weightRetrieve(month, year);
calories = 0.9 * km * weighta;
});
update+=xxx+calories+yyy;
} //for end
$("#form").html(update).trigger('create');
And this is the second function:
function weightRetrieve(month, year) {
db.transaction(function(tx) {
tx.executeSql('SELECT weight,day, ......etc) {
weight=results.rows.item(i).weight;
return weight;
}
}
I cutted out a lot of code, but the only problem I'm facing is that I cannot retrieve the weight from the second function neither this way or inside the first transaction cause if works async
It would be bad programming if I would store values with localStorage?

As a general rule, if you are using asynchronous calls, trying to "return" a value isn't going to do you much good. The outer function has usually finished executing and returned the variable well before the inner asynchronous function has put any data in the variable, so you end up returning an empty variable. Additionally in your case, your "return" is returning from the anonymous function you passed to db.transaction rather than weightRetreive.
You instead need to pass in a callback function as a parameter, then call that callback function with the value once you have it.
For example:
function weightRetrieve(month, year, onSuccess) {
db.transaction(function(tx) {
tx.executeSql('SELECT weight,day, ......etc', [], function(tx, results) {
weight=results.rows.item(i).weight;
onSuccess(weight);
});
});
}
Notice the addition of the parameter "onSuccess" as a callback function.
Because this is all in a for loop, you'll need to give each iteration the chance to finish and add its "calories" calculation to your final accumulated value before continuing. To do this you can create a function to call at the end of each iteration, which keeps track of the number of times it has been called and compares it to the length of the thing you're looping over before executing the final code:
var updateFinalValue = (function() {
var numCalls = 0; // number of times the function has been called
var totalIterations = thingYouLoopOver.length; // number of times the for loop will run
var finalValue = 0; // total of all your "calories" calculations
return function(additionalValue) {
finalValue += additionalValue;
if(++numCalls == totalIterations ) {
document.getElementById("totalCalories").value = finalValue;
doSomethingWithFinalValue(finalValue);
// etc
}
};
})();
The syntax may look a bit weird if you're not overly familiar with javascript, but it's the inner function being returned that gets executed when you call "updateFinalValue". The outer function is evaluated immediately (notice the "();" right at the end - we're calling the outer function in-place) and serves purely to provide a scope for the inner function. This is known as a "closure", and among other things is a good way to encapsulate the variables rather than having to make them global.
You would then use this as follows:
for(...) {
db.transaction(function(tx) {
tx.executeSql('SELECT ...', [], function(tx, results) {
...
...
weightRetrieve(month, year, function(weight) {
var calories = 0.9 * km * weight;
updateFinalValue(calories);
});
});
});
}

Related

using a thunk to factor time out of async code

Kyle Simpson has an amazing class on pluralsight.
In one of the modules, he goes through a snippet of code that can be safely called asynchronously, and be certain that the results are going to be shown to the user in the same sequence with which the code was executed.
The function in its glory:
function getFile(file) {
var text, fn;
fakeAjax(file, function(response){
if (fn) fn(response);
else text = response;
});
return function(cb) {
if (text) cb(text);
else fn = cb;
}
}
We can call it like so:
I'm having a tough time understanding the getFile function:
where is cb defined? how does it get called, i.e. cb(text) if it's not defined anywhere?
when we call getFile, how does the response get a value at all?
getFile returns an anonymous function:
return function(cb) {
if (text) cb(text);
else fn = cb;
}
so var th1 = getFile("file") ends up assigning that anonymous function to the value of th1, so th1 can now be called with an argument - which becomes cb. So when later, we call th1 with:
th1(function(text1) {
...
we are passing in a second anonymous function (with argument text1) which is assigned to cb (shorthand for 'callback').
The reason it works is that when the ajax is complete, it does one of two things:
if fn is defined, calls fn with the response
if not, it stores the response
Conversely, when the returned anonymous function is called, it does one of two things:
if text is defined (i.e. a result is already received) then it calls the callback with the response
if not, it assigns the callback (cb) to fn
This way, whichever happens first - ajax complete, or thunk called, the state is preserved, and then whichever happens second, the outcome is executed.
In this way, the 'thunks' can be chained to ensure that while the ajax functions happen in parallel the output methods are only called in the sequence in which the fn values are assigned.
I think part of the confusion is unclear variable naming, and the use of liberal anonymous functions with out giving them an intention revealing name. The following should be functionally equivalent with clearer naming (I think):
function getFile(file) {
var _response, _callback;
fakeAjax(file, function(response){
if (_callback) _callback(response);
else _response = response;
});
var onComplete = function(callback) {
if (_response) callback(_response);
else _callback = callback;
}
return onComplete;
}
then:
var onFile1Complete = getFile("file1");
var onFile2Complete = getFile("file2");
var onFile3Complete = getFile("file3");
var file3Completed = function(file3Response) {
output("file3Response");
output("Complete!");
}
var file2Completed = function(file2Response) {
output(file2Response);
onfile3Complete(file3Completed)
}
var file1Completed = function(file1Response) {
output(file1Response);
onFile2Complete(file2Completed);
}
onFile1Complete(file1Completed);

Javascript function wait fo other one to occur

hello Im new to javascript and I cant figure how to make function wait for other one to finish, here is my code:
// other code
db.transaction(function (transact,callback) {
transact.executeSql('SELECT * FROM buildings WHERE id = ?', [id], function (transact, results) {
if (results.rows.length > 0) {
buildingsCache.push(JSON.parse(results.rows.item(0).data));
index = buildingsCache.length - 1;
}
else {
alert("Building not found in database!");
}
});
});
return buildingsCache[index];
// other code
The problem is that current to function returns value before its set in subfunction. I would need to do something like this:
// other code
var FINISHED=false;
db.transaction(function (transact,callback) {
transact.executeSql('SELECT * FROM buildings WHERE id = ?', [id], function (transact, results) {
if (results.rows.length > 0) {
buildingsCache.push(JSON.parse(results.rows.item(0).data));
index = buildingsCache.length - 1;
}
else {
alert("Building not found in database!");
}
});
FINISHED=true;
});
while(!FINISHED);
return buildingsCache[index];
// other code
but its not working. I checked some other solutions here, but didnt make work any of it. Can you help pls?
You need to change your code so the transaction callback is the one that calls the functions that are waiting for it. Basically, your function to fetch the buildingsCache will not return a value. Instead, it will receive a callback and pass it the buildingsCache when its available. This is the same pattern that the db.transaction function uses
function fetchBuildingsCache(onDone){
db.transaction(function (transact,callback) {
transact.executeSql('SELECT FROM...', [id], function (transact, results) {
//process db results...
//then "return" by calling our callback
onDone(buildingsCache[index]);
});
});
}
//and this is how you use it:
fetchBuildingsCache(function(cache){
console.log(cache);
}
Of course, one side effect of this pattern is that every function that calls fetchBuildingsCache and all functions that call those functions and so on will also have to be converted to callback-passing style.
Sadly, this is unavoidable in Javascript. The best you can do is use a higher level library (like promises or async.js) or use one of those Javascript transpilers that add async/await syntax to Javascript.
Use promises for this purpose.
Take a look at Q js library: http://github.com/kriskowal/q
You've a callback in db.transaction(function (transact,callback), use it properly to execute next set of code once executeSql is done.
A promise represents the eventual result of an asynchronous operation. It is a placeholder into which the result will be materialized.
Use it properly callback vs. promise.

How to make a javascript FOR LOOP wait for certain conditions before looping

I have to call up a function (checkImdb) that will fetch some info from a php file (temp.php) and put some contents on a div (placeToFetchTo). This has to be done a certain amount of times, so I used a FOR LOOP for that.
The problem is that only the last instance of the looped counter (currentCastId) gets used. I understand there needs to be a way to force the FOR LOOP to wait for the fetch to be complete, and I have been looking online for answers but nothing seems to work so far. I apologise if I have missed an eventual answer that already exists.
Any help is appreciated.
This is the code I am referring to:
function checkImdb (totalCasts) {
$(function() {
for (currentCastId = 1; currentCastId <= totalCasts; currentCastId++) {
//Gets cast IMDB#
var row = document.getElementById("area2-" + currentCastId)
row = row.innerHTML.toString();
var fetchThis = "temp.php?id=" + row + "\ .filmo-category-section:first b a";
placeToFetchTo = "#area0-" + currentCastId;
function load_complete() {
var filhos = $(placeToFetchTo).children().length, newDiv ="";
var nrMoviesMissing = 0, looped = 0;
alert("done- "+ placeToFetchTo);
}
document.getElementById("area0").innerHTML = document.getElementById("area0").innerHTML + "<div id=\"area0-" + currentCastId + "\"></div>";
$(placeToFetchTo).load(fetchThis, null, load_complete);
} //End of: for (imdbLooper = 0; imdbLooper <= totalCasts; imdbLooper++) {
}); //End of: $(function() {
}
2017 update: The original answer had the callback arg as last arg in the function signature. However, now that the ES6 spread operator is a real thing, best practice is to put it first, not last, so that the spread operator can be used to capture "everything else".
You don't really want to use a for loop if you need to do any "waiting". Instead, use self-terminating recursion:
/**
* This is your async function that "does things" like
* calling a php file on the server through GET/POST and
* then deals with the data it gets back. After it's done,
* it calls the function that was passed as "callback" argument.
*/
function doAsynchronousStuff(callback, ...) {
//... your code goes here ...
// as final step, on the "next clock tick",
// call the "callback" function. This makes
// it a "new" call, giving the JS engine some
// time to slip in other important operations
// in its thread. This basically "unblocks"
// JS execution.
requestAnimationFrame(function() {
callback(/* with whatever args it needs */);
});
}
/**
* This is your "control" function, responsible
* for calling your actual worker function as
* many times as necessary. We give it a number that
* tells it how many times it should run, and a function
* handle that tells it what to call when it has done
* all its iterations.
*/
function runSeveralTimes(fnToCallWhenDone, howManyTimes) {
// if there are 0 times left to run, we don't run
// the operation code, but instead call the "We are done"
// function that was passed as second argument.
if (howManyTimes === 0) {
return fnToCallWhenDone();
}
// If we haven't returned, then howManyTimes is not
// zero. Run the real operational code once, and tell
// to run this control function when its code is done:
doAsynchronousStuff(function doThisWhenDone() {
// the "when done with the real code" function simply
// calls this control function with the "how many times?"
// value decremented by one. If we had to run 5 times,
// the next call will tell it to run 4 times, etc.
runSeveralTimes(fnToCallWhenDone, howManyTimes - 1);
}, ...);
}
In this code the doAsynchronousStuff function is your actual code.
The use of requestAnimationFrame is to ensure the call doesn't flood the callstack. Since the work is technically independent, we can schedule it to be called "on the next tick" instead.
The call chain is a bit like this:
// let's say we need to run 5 times
runSeveralTimes(5);
=> doAsynchronousStuff()
=> runSeveralTimes(5-1 = 4)
=> this is on a new tick, on a new stack, so
this actually happens as if a "new" call:
runSeveralTimes(4)
=> doAsynchronousStuff()
=> runSeveralTimes(4-1 = 3), on new stack
runSeveralTimes(3)
...
=> doAsynchronousStuff()
=> runSeveralTimes(1-1 = 0), on new stack
runSeveralTimes(0)
=> fnToCallWhenDone()
=> return
<end of call chain>
You need to use a while loop and have the loop exit only when all your fetches have completed.
function checkImdb (totalCasts) {
currentCastId = 1;
totalCasts = 3;
doneLoading = false;
while (!doneLoading)
{
//do something
currentCastId++;
if (currentCastId == totalCasts)
doneLoading = true;
}
}

Returning data from looped nested AJAX queries

I have a nested set of ajax calls, something like:
function getSubcategories(cat,callback) {
$.ajax({
url:'myurl.php',
data:'q='+cat,
dataType='json',
success:function(result){ callback(result) }
});
}
function getSubcatElements(subcat,callback) {
$.ajax({
url:'myurl2.php',
data:'q='+subcat,
dataType='json',
success:function(result){ callback(result) }
});
}
function organizeData(cat,callback) {
getSubcategories(cat,function(res){
totals=0;
list=new Array;
$.each(res['subcat'],function(key,val){
getSubcatElements(val,function(items){
$.each(items['collection'],function(key2,val2) {
list.push(val2['descriptor']);
});
totals+=items['count'];
// If I shove "totals" and "list" into an object here to callback, obviously gets called many times
}
// If I return an object here, it doesn't actually have counts from the asynchronous call above
}
function doStuff(cat) {
organizeData(cat,function() {
//stuff
});
So I'm running a looped asynchronous query that's a child of another asynch query, and I want the final result of the child loop without being "lazy". Right now I have it just returning updated results so the numbers change a few times, but I'd like to do it in one fell swoop.
It seems that the obvious place to do it would be to store the results in the asynch and return it after the $.each(), but JavaScript is insane and scoffs at things like obviousness. I feel like this should involve $.Deferred() but the samples I found all seemed like they should trigger after the first iteration ...
(The functions are deliberately separated as there is sometimes reason to use only one or only the other).
Thanks in advance!
Right now, your approach is fine. I want to add following changes in your code
function organizeData(cat, callback) {
getSubcategories(cat, function(res) {
totals = 0;
list = new Array();
totalSubCatItem = res['subcat'].length;
currentSubCatItem = 0;
$.each(res['subcat'], function(key, val) {
getSubcatElements(val, function(items) {
$.each(items['collection'], function(key2, val2) {
list.push(val2['descriptor']);
});
totals += items['count'];
// If I shove "totals" and "list" into an object here to callback, obviously gets called many times
// Here the solution
currentSubCatItem++;
if(currentSubCatItem === totalSubCatItem){
callback(/** pass argument here **/)
}
});
// If I return an object here, it doesn't actually have counts from the asynchronous call above
});
})
}
function doStuff(cat) {
organizeData(cat, function( result) {
//stuff
console.log(result)
});
}
First, you should probably organize your database query on the server side so it's returning a single, multi-plexed result. Rather than calling it lots of times.
Barring that, and assuming you don't know how many sub-categories you're going to call until your category call returns, your best bet is to create a global var that counts up every time it makes a call, and then counts down every time the callback receives a result. Whenever callback fires, counts down, and the new count is zero, do your updates.

Function not being called synchronously?

I'm having a problem with some code not performing as expected, I should probably explain what it's doing first:
On document load function selectForLists is querying a sqlite DB
containing football scores, specifically a table called matches, then
calling function renderLists.
RenderLists puts the playing team into a sorted list with duplicates
removed.
Then for each entry in this list of teams function latestTest is
being called, which selects all rows from the match table where that
team is playing and calls latestTest2.
LatestTest2 counts the number of rows with that team playing and
outputs some code to the inserted div.
Once that has been completed for every team it should revert to
finish the renderLists function and call the loaded function, except
it doesnt and I have to add a delay to calling this function because
it doesnt happen last.
I'm hoping someone can tell me whats wrong here, why the loaded function isn't called after all the above is completed? Also if anyone has any tips for achieving the same outcome with more efficient code I would like that very much.
Apologies for this long post, I'm sure many will find the code terrible and I know there are too many functions and probably many better ways of doing this but its been a few years since working with javascript in uni and I'm struggling with it and sqlite.
The code is below or at http://pastebin.com/7AxXzHNB thanks
function selectForLists() { //called on (document).ready
db.transaction(function(tx) {
tx.executeSql('SELECT * FROM matches', [], renderLists);
});
}
function renderLists(tx, rs) {
var playingList = new Array();
for (var i = 0; i < rs.rows.length; i++) {
playingList.push(rs.rows.item(i)['playing']);
}
playingListSort = playingList.sort();
var playingListFinal = new Array();
playingListSort.forEach(function(value) {
if (playingListFinal.indexOf(value) == -1) {
playingListFinal.push(value);
}
});
for (var c = 0; c < playingListFinal.length; c++) {
latestTest(playingListFinal[c]);
}
loaded(); //not running last in the function
//setTimeout(loaded,1000);
/////Using a delay because it doesn't run after the above has completed
}
function latestTest(team) {
db.transaction(function(tx) {
tx.executeSql('SELECT * FROM matches WHERE playing="' + team + '"', [], latestTest2);
});
}
function latestTest2(tx, rs) {
counted = rs.rows.length;
var theFunction = rs.rows.item(0)['playing'];
$('#inserted').append('<li onclick="onToDate(\'' + theFunction + '\')"><img width="30px" height="25px" id="popupContactClose" src="style/soccer.png"><div id="popupContactClose2">' + counted + '</div></img>' + rs.rows.item(0)['playing'] + '</li>');
}
Both db.transaction and tx.executeSql are asynchronous functions, just like setTimeout, where if you write
setTimeout(function(){
doLater();
}, 1000)
doNow();
doNow() will execute before doLater() because the callback function you created will get called at some future time.
In your case, latestTest() calls db.transaction, and then tx.executeSql, both of which are asynchronous. That means, the callback function latestTest2 will get called at some future time, which will be after when loaded() gets called.
The latestTest function calls another executeSQL function with its own callback. That callback will be executed when the SQL has finished, which will be at an arbitrary time.
The renderLists function will continue execution (including calling the loaded function) normally, quite apart from anything having to do with the callbacks in latestTests being executed.
Your mistake is thinking that loaded will "wait" to be executed--you will still have pending callbacks from the DB code in latestTest.

Categories

Resources