I am using Cordova and Angular for a small webapp. Here is the implementation process:
LogsModel is created
queryLogs() is called
getLogsArray() is called – at this point, even in the function definition, console.log(this.logs['0']) returns undefined. I do not understand why.
LogsModel.prototype.logs = {};
LogsModel.prototype.getLogsArray = function (){
console.log(this.logs['0']);
return this.logs;
};
LogsModel.prototype.queryLogs = function (){
var self = this;
function getFromDb (tx){
tx.executeSql('SELECT * FROM DEMO',[],querySuccess,self.query_fail);
}
function querySuccess(tx,results){
var len = results.rows.length;
for(var i = 0; i < len; i++){
var log = {
verb: results.rows.item(i).verb
};
self.logs[i]= {
verb: results.rows.item(i).verb
};
}
}
this.db = window.openDatabase("Database","1.0","Cordova Demo",200000);
this.db.transaction(getFromDb,self.transaction_fail);
};
queryLogs is an asynchronous task. Call getLogsArray directly after starting the transaction and the array won't be populated yet.
Instead, use a callback which you invoke from the querySuccess function to continue processing (e.g. logging results).
Related
I'm trying to count the number of elements returned from $resouce.query() object, so that I can increment it and use it as the ID for the next object to be saved.
Following is a service to communicate with the server:
eventsApp.factory('EventData', function($resource) {
var resource = $resource('/data/event/:id', {id: '#id'});
return {
getEvent: function(eventId) {
return resource.get({id: eventId});
},
saveEvent: function(event) {
var count = 0;
resource.query(function(data) {
count = data.length; // Accessible here!
});
event.id = count; // Not accessible here!
return resource.save(event);
},
getAllEvents: function() {
var count = 0;
var lol = resource.query(function(data) {
count = data.length;
});
console.log(count);
return resource.query();
}
}
});
However, as mentioned in the comments, I'm unable to access the length property. Any solutions?
By looking at your code what I am getting is your resource.query executes the callback function asynchronously because of that your event.id = count; is executed first and then the callback it executed. If you want to access data.length then you can use $q and create a defer. And then resolve that deferred object.
I have this piece of code:
for(var i = 0; i < some_array.length; i++){
some_array[i].asynchronous_function(some, parameter, callback(){
some_procedure();
});
}
I call asynchronous_function for each element of the array, and once the function executed, it fires a callback. I have some procedure in the callback that I would like to execute only if this callback is the last one returning of all the asynchronous_functions called. Is there a way to achieve this without polluting too much the code?
Thanks
count the number of times asynchronous_function is called. when it has been called some_array.length times, you can call some_procedure(). something like this
var numTimesCalled = 0;
for(var i = 0; i < some_array.length; i++){
some_array[i].asynchronous_function(some, parameter, function(){
numTimesCalled ++;
if (numTimesCalled === some_array.length) {
some_procedure()
}
});
}
This should do the job :
// callAll : calls methodName method of all array items.
// uses the provided arguments for the call and adds the callback
// methodName is async and must accept a callback as last argument
// lastCallBack will get called once after all methods ended.
//
function callAll(anArray, methodName, lastCallBack ) {
// create closure to keep count of calls
var callCount = anArrray.length;
// build function that calls lastCallBack on last call
var callIfLast = function() { callCount--; if (!callCount) lastCallBack(); };
// build function arguments
var args = arguments.slice(3).push(callIfLast);
// call all functions
anArray.forEach( function(item) { item[methodName].apply(item, args ) } );
}
callAll(myArray, 'meth', myCallback, 1, 2, 3);
// ...
// --> will call myArray[i].meth(1, 2, 3, callIfLast) for all i.
// and call myCallBack when all calls finished.
I wrote a callback helper, that lets me group multiple callbacks into one function variable:
function chainCallbacks() {
var callbacks = arguments;
return function () {
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] != null) {
callbacks[i].apply(null, arguments);
}
}
};
}
this works, but I'm wondering if there are any javascript libraries that provide the same functionality? or even better, something that simulates the .NET "event" pattern?
myEvent+=myCallback;
I have modified your chainCallbacks function. You can test below code in JS console (I'm using Chrome -works fine), and check the result.
var result = 0;
function a() {
result += 5;
console.log(result);
_next();
}
function b() {
result += 10;
console.log(result);
_next();
}
function c() {
result += 20;
console.log(result);
_next();
}
function chainCallbacks() {
var _this = this;
var _counter = 0;
var _callbacks = arguments;
var _next = function() {
_counter++;
if(_counter < _callbacks.length) {
_callbacks[_counter].apply(_this);
}
};
_this._next = _next;
return function() {
if(_callbacks.length > 0) {
_callbacks[0].apply(_this);
}
};
}
var queue = chainCallbacks(a, b, c);
queue();
Idea is simple - you call _next() whenever your callback function has finished executing, and you want to jump to another. So you can call _next() e.g. after some jQuery animation as well, and this way you will preserve the order of the functions.
If you want to replace a callback with one that calls the original as well as some others, I'd probably just do something like this:
Requirejs.config.callback = function(orig) {
var fns = [orig, first, second, third];
return function() {
fns.forEach(function(fn) { fn.apply(null, this); }, arguments);
};
}(Requirejs.config.callback);
But if you're doing this often, I think your solution will be as good as it gets. I don't see need for a library.
Requirejs.config.callback = chainCallbacks(Requirejs.config.callback, first, second, third)
A library can't do anything to extend language syntax in JavaScript. It's limited to what's available... no operator overloading or anything.
I am having problem with calling the generated functions in serial. I am using the async library and the code seems to work when there is no deep callback calling needed. When I add the real scenario it throws errors.
Here is the example which works, returns the array of 0 to 4:
Scrape.prototype.generatePageFunctions = function() {
var functionList = new Array(), self = this;
for (var i = 0; i < this.pageSet; i++) {
(function(i) {
functionList.push(function(cb) {
// Inner functions which will be called in seriers
var timeoutTime = parseInt(Math.random() * 5000 + 3000, 10);
setTimeout(function() {
self.setIndex(i);
//self.getSite(function)
cb(null, i);
}, timeoutTime);
});
})(i);
}
return functionList;
}
Scrape.prototype.run = function() {
var functionList = this.generatePageFunctions();
async.series(functionList, function(err, results) {
console.log('Job is completed ');
console.log(results);
});
}
Now adding the real scenario like downloading the site and then put in callback:
Scrape.prototype.generatePageFunctions = function() {
var functionList = new Array(), self = this;
for (var i = 0; i < this.pageSet; i++) {
(function(i) {
functionList.push(function(cb) {
// Inner functions which will be called in seriers
var timeoutTime = parseInt(Math.random() * 5000 + 3000, 10);
setTimeout(function() {
self.setIndex(i);
self.getSite(function(result) {
// Async callback to pass the data
cb(null, result);
});
}, timeoutTime);
});
})(i);
}
return functionList;
}
Error is like this, even if passing instead of result iterator variable i:
/home/risto/scrape/node_modules/async/lib/async.js:185
iterator(x.value, function (err, v) {
^
TypeError: Cannot read property 'value' of undefined
at /home/risto/scrape/node_modules/async/lib/async.js:185:23
at /home/risto/scrape/node_modules/async/lib/async.js:108:13
at /home/risto/scrape/node_modules/async/lib/async.js:119:25
at /home/risto/scrape/node_modules/async/lib/async.js:187:17
at /home/risto/scrape/node_modules/async/lib/async.js:491:34
at /home/risto/scrape/scraper/scrape.js:114:13
at /home/risto/scrape/scraper/scrape.js:64:16
at Object.<anonymous> (/home/risto/scrape/scraper/engines/google.js:58:12)
at Function.each (/home/risto/scrape/node_modules/cheerio/lib/api/utils.js:133:19)
at [object Object].each (/home/risto/scrape/node_modules/cheerio/lib/api/traversing.js:69:12)
// Edit
Only result which gets added into the complete callback is the first one, other functions are never called.
Also for information the functions return object literals if that does matter.
There is nothing wrong with your code. Creating a simple testcase shows that.
I created a mock:
Scrape = function() {
this.pageSet = 5;
}
Scrape.prototype.setIndex = function() {
}
Scrape.prototype.getSite = function(cb) {
cb('works');
}
and calling the run method it outputs the expected:
[ 'works', 'works', 'works', 'works', 'works' ]
So the problem is somewhere else. Have you tried to check the functionList variable in your run method?
Thank you #KARASZI István, all the code above is correct, the problem seemed in somewhere else. The deepest callback got called multiple times but the outer one got called only once.
whenever i try to run something like the following, firebug tells me that "markers is undefined" at the line "for (var i=0 ..."
but i declared markers as a global variable at the top right...?
var markers;
function load() {
$.get("phpsqlajax_genxml.php", function(data) {
markers = data.documentElement.getElementsByTagName("marker");
});
for (var i = 0; i < markers.length; i++) {
var name = markers[i].getAttribute("name")
//do more stuff
}
}
but when i do this, it works.
var markers;
function load() {
$.get("phpsqlajax_genxml.php", function(data) {
markers = data.documentElement.getElementsByTagName("marker");
makeMarkersWithXMLinfo();
});
function makeMarkersWithXMLinfo() {
for (var i = 0; i < markers.length; i++) {
var name = markers[i].getAttribute("name")
//do more stuff
}
}
}
i'm not even passing "markers" as an argument to my makeMarkersWithXMLinfo() function. but yet it works. what's going on? thnx
The problem you're having is that get starts an asynchronous operation. So your code immediately following the call to get happens before the success callback on the get is run. E.g. (see the comments):
var markers;
function load() {
// ===> This happens FIRST
$.get("phpsqlajax_genxml.php", function(data) {
// ===> This happens THIRD, some time after `load` returns
markers = data.documentElement.getElementsByTagName("marker");
});
// ===> This happens SECOND
for (var i = 0; i < markers.length; i++) {
var name = markers[i].getAttribute("name")
//do more stuff
}
}
Your second example is the correct way to code it (although I'd recommend avoiding a global entirely), because you're using markers only after the GET has completed.
$.get is asynchronous, that means that if you call something immediatly after $.get, it's callback function wouldn't be invoked yet, and your global would still be undefined.