Javascript function wait fo other one to occur - javascript

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.

Related

How to wait for callback while iterating an array

I am working with a transnational framework within Javascript. So I need to wait for the previous query to finish before I move on. For example...
// Explicit this won't work because the length is not static
var i = [1,2,3]
doSomething(i[0], function(){
doSomething(i[1], function(){
doSomething(i[2], function(){
commitTransaction()
}
})
})
From this example I can't figure out a way to do this dynamically. It feels like a queue/recursion problem but I can't seem to crack it.
Does anyone else have an idea? I can also wrap in promises so that is an option as well, although that seems less synchronous.
Use async.eachSeries. So your code would translate to:
var transaction = {...};
async.eachSeries([1, 2, 3], function(value, callback) {
doSomething(value, transaction, callback);
}, function(err) {
if(err) throw err; // if there is any error in doSomething
commitTransaction(transaction);
});
jsFiddle Demo
I would suggest making a queue to do this. It would take the array, the generic callback function and a final function to callback with. Basically, the best way to accomplish this is to allow your functions to expect to have values injected.
The core assumption is that it is understood the caller will allow their callback function to have the current value and next callback function injected. That basically means we will end up with a function I have named queueAll which looks like this
function queueAll(arr,cbIteration,final){
var queue = [function(){ cbIteration(arr[arr.length-1],final) }];
for(var i = arr.length-2; i > 0; i--){
(function(next,i){
queue.unshift(function(){ cbIteration(arr[i],next) });
})(queue[0],i)
}
cbIteration(arr[0],queue[0]);
}
It takes the final call, places it in the queue, and then iterates, placing subsequent callback functions in the queue with the current value closed over, as well as closing over the front of the queue which at that point is the next call back. It is fairly simple to use. Pass it an array, a callback which expects values to be injected, and a final function.
In your case it would look like
queueAll(i,function(item,next){
doSomething(item,next);
},function(){
commitTransaction();
});
Stack Snippet Demo
//## <helper queue>
function queueAll(arr,cbIteration,final){
var queue = [function(){ cbIteration(arr[arr.length-1],final) }];
for(var i = arr.length-2; i > 0; i--){
(function(next,i){
queue.unshift(function(){ cbIteration(arr[i],next) });
})(queue[0],i)
}
cbIteration(arr[0],queue[0]);
}
//## </helper queue>
//## <user defined functions>
function doSomething(val,callback){
setTimeout(function(){
console.log(val);
callback();
},val*10);
}
function commitTransaction(){
console.log("commit");
}
//## </user defined functions>
//## <actual use>
var arr = [10,20,30];
queueAll(arr,function(item,next){
doSomething(item,next);
},function(){
commitTransaction();
});
//## </actual use>
Actually, I think promises are exactly what you're looking for. But for a traditional callback approach, consider the following:
var state = false,
doSomething = function (value, callback) {
/* do stuff with value */
if (!state)
doSomething(newValue, callback);
else
callback();
};

Synchronous WebSQL Transaction with another transaction inside

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);
});
});
});
}

Return object into async.each function in node.js

I want to understand one thing about async module in node.js.
I have created a function that map an object from a form to a model object and return this object.
This object is a video with an array of tags.
My question is where can I return the video ? I know normally it is inside the async callback function but if I do that, the object returned is undefined.
Whereas If i return the video object at the end of the whole function, it works but it's not safe as I'm not sure, my async is finished...
By the way, I don't understand the callback function passed in argument to async.each and
called after video.products.push(tag); . What does this function do?
Regards
in my mapping.js :
exports.video = function(object) {
var video = new Video();
video.name = object.name;
video.products = [];
async.each(object.tags, function(tago, callback) {
tag = {
"name" : tago.name
}
video.products.push(tag);
callback();
} ,
function(err) {
if( err ) {
console.log('Error' + error);
throw err;
}
logger.debug("into async" + video);
}
);
logger.debug("end function " );
**//return video;**
}
in my video.js :
var video = mapping.video(object);
logger.debug(video); // return undefined
The simple answer is that you can't - at least not via easy or obvious approach. As its name suggests, async is a library for queuing up asynchronous function calls into the event loop. So your exports.video function simply kicks off a bunch of asynchronous functions, which execute one after the other on an unpredictable time-frame, and then returns immediately. No matter where you try to return your video object within the scope of your function calls which are instantiated by async, the exports.video function will already have returned.
In this case it doesn't really seem like you need asynchronous function calls for what you're doing. I'd suggest that you replace your use of async with something like Underscore's each method, which executes synchronously, instead.
http://documentcloud.github.io/underscore/#each
You'd need to define a callback for your exports.video function e.g..
exports.video = function(object, callback) {
// video code (snip)...
async.each(object.tags,
function eachTag(tag, done) {
// code run for each tag object (snip)...
done();
},
function finished(err) {
// code run at the end (snip)...
callback(thingThatsReturned);
});
};
...and call it like this:
var videoUtils = require('videoUtils');
var tags = getTags();
videoUtils.video({ tags: tags }, function(thingThatsReturned) {
// do something with 'thingThatsReturned'
});
By the way, I don't understand the callback function passed in
argument to async.each and called after video.products.push(tag); .
What does this function do?
The async.each function will call the 'eachTag' function above (2nd argument) for each item in your array. But because it's done asynchronously, and you might do something else async in the function (hit a database/api etc.), it needs to know when that function for that particular array item has finished. Calling done() tells async.each that the function has finished processing. Once all the functions are finished processing (they've all called done()), async.each will run the 'finished' function above (3rd argument).
This is pretty standard async stuff for Node.js, but it can be tricky to get ones head around it at first. Hang in there :-)
Edit: It looks like your code isn't doing anything asynchronous. If it was, then the above code would be the way to do it, otherwise the following code would work better:
exports.video = function(object) {
// video code (snip)...
if (Array.isArray(object.tags)) {
object.tags.forEach(function eachTag(tag) {
// code run for each tag object (snip)...
});
}
return thingThatsReturned;
};
...and call it...
var videoUtils = require('videoUtils');
var tags = getTags();
var thingThatsReturned = videoUtils.video({ tags: tags });

Underscore js times series

I'm using UnderscoreJs with nodejs and have a need for the _.times() method. times() will invoke a function X number of times
This works as expected, however I need to iterate in a series, instead of in parallel which this appears to be doing.
Any idea if there's a way to use this in series w/ callback methods?
Given something like this:
function f() {
some_async_call({ callback: function(err, results) {...})
}
_(3).times(f);
Then the three f calls will happen in series but the some_async_call calls won't necessarily happen in series because they're asynchronous.
If you want to force your calls to run in series then you need to use the callback on the async call to launch the next one in the series:
function f(times, step) {
step = step || 0;
some_async_call({
callback: function(err, results) {
// Do something with `err` and `results`...
if(step < times)
f(times, step + 1);
}
});
}
f(3);
That approach will execute the three some_async_calls in series but, alas, the initial f(3) will return immediately. One solution to that problem is, of course, another callback:
function f(from_n, upto, and_finally) {
some_async_call({
callback: function(err, results) {
// Do something with `err` and `results`...
if(from_n < upto)
f(from_n + 1, upto, and_finally);
else
and_finally();
}
});
}
f(0, 3, function() { console.log('all done') });
Where does _.times in with all this? No where really. _.times is just a for loop:
_.times = function(n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
};
_.times exists for completeness and to allow you to add for loop when using _.chain. You could probably shoe-horn it in if you really wanted to but you would be making a big ugly mess instead of simplifying your code.
You could use 250R's async idea but you'd have to build an array of three functions but _.range and _.map would be more appropriate for that than _.times:
// Untested off the top of my head code...
function f(callback) {
some_async_call({
callback: function(err, results) {
// Deal with `err` and `results`...
callback();
}
});
}
var three_fs = _(3).range().map(function() { return f });
async.series(three_fs);
But you still have to modify f to have a callback function and if you're always calling f three times then:
async.series([f, f, f]);
might be better than dynamically building the array with _.range and _.map.
The real lesson here is that once you get into asynchronous function calls, you end up implementing all your logic as callbacks calling callbacks calling callbacks, callbacks all the way down.
This async library might get you started
https://github.com/caolan/async#series
Or if you want to do it yourself, the idea is to do recursive calls after each function callback is called, here's the source code https://github.com/caolan/async/blob/master/lib/async.js#L101

How do I wrap executions of asynchronous (callback-based) functions into a synchronous function in Javascript?

I'm trying to write a function in Javascript (with jQuery, if you want):
function fetchItem(itemId) { return /* ??? */; }
This function relies on a second, predefined and unmodifyable function that looks like this:
function load(callback) { /* ... */ }
This function is asynchronous. After calling it, it fetches n items via XHR, then when they have arrived, stores them in the DOM, then invokes the callback.
fetchItem uses a simple jQuery selector (irrelevant here) to check the DOM for the element with itemId and calls load if the item isn't there yet. Rinse and repeat.
My problem is that I want to wrap multiple asynchronous calls of load into my synchronous fetchItem function, which should return the DOM element with itemId after it has made enough load calls.
Pseudo code, if load was synchronous:
function fetchItem(itemId):
while not dom.contains(itemId):
load()
return dom.find(itemId)
My first attempts at doing this in Javascript, which probably display a lot of misconceptions about Javascript's closures and execution model: ;)
function fetchItem(itemId) {
var match = undefined;
function finder() {
match = $(...).get(0);
if(!match) {
load(finder);
}
}
finder();
return match;
}
Obviously, this fails because the return is executed before the first callback. Also, as you can see I had some problems getting match back out to fetchItem. Is it properly protected by the closure here? Would this work if fetchItem was executed multiple times in parallel, assuming that load supports this (and doesn't mix up the DOM)?
I'm probably missing a perfectly good pattern here, but I don't really know what to google for...
You need to make fetchItems async too and provide it a callback, something like this should probably work (warning untested!):
function fetchItems(itemIDS, callback, matches) {
if (!matches) { // init the result list
matches = [];
}
// fetch until we got'em all
if (itemIDS.length > 0) {
var id = itemIDS[0]; // get the first id in the queue
var match = $(id).get(0);
// not found, call load again
if (!match) {
load(function() {
fetchItems(itemIDS, callback, matches);
});
// found, update results and call fetchItems again to get the next one
} else {
matches.push(match); // push the current match to the results
itemIDS.shift(); // remove the current id form the queue
fetchItems(itemIDS, callback, matches);
}
// we have all items, call the callback and supply the matches
} else {
callback(matches);
}
}
fetchItems(['#foo', '#bar', '#test'], function(matches) {
console.log(matches);
})
I would simply gave your fetchItem function as a callback to load. Like this:
function fetchItem(itemId, callback):
if not dom.contains(itemId):
load(fetchItem)
else:
callback(dom.find(itemId))
callback() is a function that does rest of the job when necessary element appears in the DOM.
That is impossible. You cannot create synchronousness from asynchronousness. Why do not you add a callback to your fetchItem-function as well?
Seems like everybody agrees that I need to introduce my own callback, so here's my (so far final) working solution:
var MAX_FETCH_MORE = 3;
/*
* Searches for itemId, loading more items up to MAX_FETCH_MORE times if necessary. When
* the item has been found or the maximum reload count has been reached, the callback
* is invoked, which is passed the DOM object of the item wrapped in a jQuery object, or
* undefined.
*/
function executeWithItem(itemId, callback, fetchCycleCounter) {
// initialize fetchCycleCounter on first iteration
if(!fetchCycleCounter) fetchCycleCounter = 0;
console.debug('iteration ' + fetchCycleCounter + '/' + MAX_FETCH_MORE);
// try to find the item in the DOM
match = $('div[data-item-id="' + itemId + '"]').get(0);
if(match) {
// if it has been found, invoke the callback, then terminate
console.debug('found: ' + match);
callback($(match));
} else if(!match && fetchCycleCounter < MAX_FETCH_MORE) {
// if it has not been found, but we may still reload, call load() and pass it
// this function as the callback
console.debug('fetching more...');
load(function() {executeWithItem(itemId, callback, fetchCycleCounter+1);});
} else {
// give up after MAX_FETCH_MORE attempts, maybe the item is gone
console.debug('giving up search');
}
}
// example invocation
executeWithItem('itemA01', function(item) {
// do stuff with it
item.fadeOut(10000);
});
Thanks to everybody for encouraging me to introduce another callback, it hasn't turned out looking so bad. :)

Categories

Resources