In specific, I'm making a call to the database which retrieves an aggregate value. This is then displayed to the user. But before the adapter call(since I'm using worklight) can complete,the code after that gets executed which is something that I want to block. I have tried everything from setTimeout to calling a function that executes an empty while loop. None of them seem to work as the code is skipping all of these too. Could someone please help me out with this?
Sample code:
var flow_completed_percentage=0;
function setFlowStatus()
{
var flow_name,flow_status_value;
for(var i = 0;i < 2; i++)
{
flow_name=flow_name_DB[i].trim();//flow_name_DB is an array that stores the flow names
flow_status_value=flow_status_DB[i].trim();//flow_status_DB is an array that stores the flow status values
if(flow_status_value=="R" || flow_status_value=="A")
{
var invocationData_totalPercentage = {
adapter : "dummy_adapter",
procedure : "getJobPercentage",
parameters : [flow_name]
};
WL.Client.invokeProcedure(invocationData_totalPercentage, {
onSuccess : setPercentageSuccess,
onFailure : setPercentageFailure
});
}
}//end of for loop
//the problem is next iteration of for loop begins before the adapter call for the current iteration completes.
//So flow_name gets overwritten and it gives the wrong result.
function setPercentageSuccess(total_percentage)
{
var tot_percent = total_percentage.invocationResult.resultSet[0];
flow_completed_percentage=tot_percent.TOT_PERCENT;
if(flow_status=="R")
runningStatus(flow_name);
else if(flow_status=="A")
abortedStatus(flow_name);
}
function setPercentageFailure()
{
alert("Failed to fetch records from DB");
}
}
function runningStatus(flow)
{
//do something
}
function abortedStatus(flow)
{
//do something
}
You must call the remaining block of code after the completion of you database operation.
Since have to perform the same operation number of times, you can call the function multiple times instead of using a loop.
I tried to change the code as follows: You can call the function with next value of i after the completion of database operation.
var flow_completed_percentage=0;
function setFlowStatus()
{
var flow_name,flow_status_value;
var initial = 0;
iterator(initial); // initial call
function iterator(i)
{
flow_name=flow_name_DB[i].trim();//flow_name_DB is an array that stores the flow names
flow_status_value=flow_status_DB[i].trim();//flow_status_DB is an array that stores the flow status values
if(flow_status_value=="R" || flow_status_value=="A")
{
var invocationData_totalPercentage = {
adapter : "dummy_adapter",
procedure : "getJobPercentage",
parameters : [flow_name]
};
WL.Client.invokeProcedure(invocationData_totalPercentage, {
onSuccess : function() {
setPercentageSuccess();
iterateNext(i); // call next after DB completion
},
onFailure : function() {
setPercentageFailure();
iterateNext(i); // call next after DB completion
}
});
}
}
function iterateNext(current)
{
current= current+1;
if(current<2){ // check the loop condition
iterator(current);
}
}
function setPercentageSuccess(total_percentage)
{
var tot_percent = total_percentage.invocationResult.resultSet[0];
flow_completed_percentage=tot_percent.TOT_PERCENT;
if(flow_status=="R")
runningStatus(flow_name);
else if(flow_status=="A")
abortedStatus(flow_name);
}
function setPercentageFailure()
{
alert("Failed to fetch records from DB");
}
}
function runningStatus(flow)
{
//do something
}
function abortedStatus(flow)
{
//do something
}
Related
I'm using Azure Mobile Services and am running a javascript backend. However, since the backend is in node.js, everything is executed asynchronously and I don't know how to halt the execution of a function.
I'm trying to delete a club if there hasn't been a comment in it within the past 24 hours, and here's my code:
var clubs = tables.getTable('Club');
clubs.read(
{
success: function(club){
var now = new Date().getTime();
for(var i=0;i<club.length;i++){
var deleteClub = true;
comments.where({ClubId: club[i].id}).read(
{
success:function(comment){
var timeDiff = (now-comment[i].Time.getTime())/(1000*60*60);
console.log("Comment in table: "+timeDiff);
if(timeDiff<24){
deleteClub=false;
}
}
}
);
if(deleteClub){
console.log("deleting club: "+club[i].Title);
//clubs.del(club[i]);
}else{
console.log("saving club: "+club[i].Title);
}
}
}
}
);
The if statement executes before the delete club variable is updated, so it's always true, but I need the if statement's execution to be delayed until after all of the comments have been looped through.
Since the callback you get is asynchronous, you can't use any information you're getting in that callback in synchronous code after the where call.
Since we want to handle things on a club-by-club basis, first we'll move the handling of clubs into its own function. This avoids the problem that by the time we get our callback from read, i will have been incremented.
Your code seems to assume success is called repeatedly, once for each comment. I don't think that's likely to be the case, more likely it's called once, with a list/array of the matching comments.
If so, then splitting off club handling to its own function and then looping the found comments should do it:
var clubs = tables.getTable('Club');
clubs.read(
{
success: function(allClubs){ // <== Note plural
var now = new Date().getTime();
for (var i = 0; i < allClubs.length; i++) {
handler(now, allClubs[i]); // <== Move handling off to a handler
}
}
}
);
function handler(now, club) { // <== Unlike `i` above, `club` won't change because it's
// a function argument that we never assign to
comments.where({ClubId: club.id}).read(
{
success: function(comments){ // <== Note the plural
var deleteClub = true;
for (var i = 0; i < comments.length; ++i) {
var timeDiff = (now-comments[index].Time.getTime())/(1000*60*60);
console.log("Comment in table: "+timeDiff);
if(timeDiff<24){
deleteClub=false;
}
}
if (deleteClub){
console.log("deleting club: "+club.Title);
//clubs.del(club);
}else{
console.log("saving club: "+club.Title);
}
}
}
);
}
client.post(config.apiUrl+"cart", args, function(data,response) {
if (data.status == 'success') {
for (var key in data.data.cart_items_list) {
if (data.data.cart_items_list.hasOwnProperty(key)) {
data.data.cart_items_list[key].child.prodqty = function () {
client.get(config.apiUrl+"product/"+data.data.cart_items_list[key].child.child_id+"/quantity", function(invdata, invresponse) {
if(invdata.status == 'success'){
console.log(invdata.data);
return invdata.data;
console.log(data.data.cart_items_list[key].child.prodqty);
}
})
}
}
}
console.log(data.data.cart_items_list);
}
})
Above is piece of code I have written to get one modify the data I got from api client call.
The first api call will a json data. I am looping thru that data to get one value from another api and append to parent's json data
console.log(data.data.cart_items_list[key].child.prodqty); line prints the correct value in my logs, but console.log(data.data.cart_items_list); this is not having the newly(child.prodqty) appended value in it.
I am very much new in Node.js so I don't know whether I am doing it correct
EDIT:
if I console.log(data.data.cart_items_list); the output has prodqty in it. but its coming like this. prodqty: [Function]
For default you say that data.data.cart_items_list get prodqty: [Function]. If you get the prodqty value you need to set this value in data.data.cart_items_list, thus you can catch it after the loop. I my opinion, I prefer to create a new var when I set it:
client.post(config.apiUrl+"cart", args, function(data,response) {
var prodqty = {};
if (data.status == 'success') {
for (var key in data.data.cart_items_list) {
if (data.data.cart_items_list.hasOwnProperty(key)) {
data.data.cart_items_list[key].child.prodqty = function () {
client.get(config.apiUrl+"product/"+data.data.cart_items_list[key].child.child_id+"/quantity", function(invdata, invresponse) {
if(invdata.status == 'success'){
console.log(invdata.data);
return invdata.data;
prodqty.key = data.data.cart_items_list[key].child.prodqty; //prodqty associated with his key
console.log(data.data.cart_items_list[key].child.prodqty);
}
})
}
}
}
console.log(JSON.stringify(prodqty));
console.log(data.data.cart_items_list);
}
})
I`ve added a object prodqty. Then I set the prodqty whit his key. I this structure of code there may be a problem. The api use a callback, then when loop finish is posible that the prodqty object doesn´t set the values. For resolve it, You need pass a callback at the second api or wait a time until second api finish:
setTimeout(function(){ console.log(JSON.stringify(prodqty)); }, 3000);` //3 seconds for example
I use Parse in iOS to run a cloud code method that gets an ID in it's request and receives a number in the response.
The purpose of the cloud code function is to take the request ID and add it to a field of 3 different users.
Here is the cloud code method in Javascript:
amount = 3;
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("addToIDs", function(request, response) {
var value = request.params.itemId;
var query = new Parse.Query(Parse.User);
query.ascending("createdAt");
query.limit(100);
query.find({
success: function(results) {
var sent = 0;
for (var i = 0; i < results.length; i++) {
var idlst = results[i].get("idString");
if (idlst != null && idlst.indexOf(value) <= -1) {
idlst += value+"|";
results[i].set("idString", idlst);
results[i].save();
sent = sent+1;
}
if (sent >= amount) {
break;
}
}
response.success(sent);
},
error: function() {
response.error("Test failed");
}
});
});
When running this cloud code method I get a response of '3' meaning it called .save for 3 users. The problem is that when i go back to look in the Database viewer in the parse website it actually only updated a single user (Its always the same user). No matter how many times i run this code, it will only actually update the first user..
Anyone know why this is happening?
Both save and saveAll are asynchronous, so you should make sure the saving process is done.
Also note that, the user object can only be updated by the owner or request with masterkey.
The following code should work:
var amount = 3;
Parse.Cloud.define("addToIDs", function(request, response) {
var value = request.params.itemId;
var query = new Parse.Query(Parse.User);
query.ascending("createdAt");
query.limit(100);
return query.find()
.then(function(results) { // success
var toSave = [];
var promise = new Parse.Promise();
for (var i = 0; i < results.length; i++) {
var idlst = results[i].get("idString");
if (idlst != null && idlst.indexOf(value) <= -1) {
idlst += value+"|";
results[i].set("idString", idlst);
toSave.push(results[i]);
}
if (toSave.length >= amount) {
break;
}
}
// use saveAll to save multiple object without bursting multiple request
Parse.Object.saveAll(toSave, {
useMasterKey: true,
success: function(list) {
promise.resolve(list.length);
},
error: function() {
promise.reject();
}
});
return promise;
}).then(function(length) { // success
response.success(length);
}, function() { // error
response.error("Test failed");
});
});
The reason this is happening is two-fold:
save() is an asynchronous method, and
response.success() will immediately kill your running code as soon as it's called.
So what's happening is that inside your for loop you're running save() several times, but since it's asynchronous, they're simply thrown into the processing queue and your for loop continues on through. So it's quickly throwing all of your save()'s into the processing queue, and then it reaches your response.success() call but, by the time it's reached, only one of the save()'s has had a chance to successfully process.
I'm trying to build an application with Ionic Framework and Javascript, and I want to fetch all the data from a table in an array, but when I try to access that array outside the function it's empty. I've read something that maybe the array gets populated after the console.log("length "+ $scope.cards.length); but I don't know how to solve this so I can use the array after that.
This is the function for fetching the data from the table(it works):
fetchCards: function(success) {
database.transaction(function(tx) {
tx.executeSql("SELECT cardId, name, chosen, filePath, found FROM Cards", [],
function (tx, results) {
console.log('success select ' + results);
cards = [];
for (i = 0; i < results.rows.length; i++) {
cards.push(results.rows.item(i));
}
success(cards);
},
function () {
console.log('error select');
});
});
}
This is where I try to access the array, in a controller:
.controller('kindergartenController', function($scope, $ionicSideMenuDelegate,service) {
//list with the difficulties
$scope.difficultyList=[
{text:"Easy", value:"easy"},
{text:"Medium", value:"medium"},
{text:"Hard", value:"hard"}];
$scope.data={difficulty:''};
//redirecting if it presses
$scope.goToAnimalsGame = function() {
if($scope.data.difficulty=='easy'){
window.location = "#/menu/animalseasy";
//in this array I want the results
$scope.cards=[];
game = new Game(1,"easyAnimalGame");
console.log("created a new game " + game.getName());
game.createMemoryGame();
console.log("difficulty " + $scope.data.difficulty);
randomNumbers=game.randomNumbers($scope.data.difficulty);
console.log('Random numbers are:');
for(i=0;i<randomNumbers.length;i++){
console.log(randomNumbers[i]);
}
//here is where I call the other function, to get data from database
service.fetchCards(function(cards) {
$scope.cards = cards;
//it shows the good length
console.log("length "+ $scope.cards.length);
});
//it shows it's empty
console.log("length "+ $scope.cards.length);
}
Any suggestions are helpful, thank you.
It will show empty because:
service.fetchCards
is Asynchronous, therefore inside that function where you define your anonymous callback, won't fire till the data is retrieved.
the console.log("length ".... outside of the function will be executed immediately after the function call fetchCards, but possibly before the callback where the array gets populated.
Unfortunately you can only deal with the populated array within the callback or from a function fired within the callback.
Here is a timeline of execution to aid:
service.fetchCards();
->
console.log("length "....) below the above function
->
anonymous callback (function (cards){}) inside the service.fetchCards()
If that makes sense.
The asynchronism of the service means that the callback you defined anonymously could fire at any time.
The solution:
service.fecthCards(function (data) {
// do your code
$scope.cards = cards;
//it shows the good length
console.log("length "+ $scope.cards.length);
// manipulate the array within this function or get it to call another function like so
nowContinue();
});
function nowContinue() {
// continue what ever it was that you wanted to do using the newly populated information
}
Because nowContinue is called with in the anonymous callback it will fire whenever the callback gets fired, and will execute after you have populated the controllers local variables with data.
I've noticed that the size of a file requested will effect how long the response takes for ajax calls. So if I fire 3 ajax GET requests for files of varying size, they may arrive in any order. What I want to do is guarantee the ordering when I append the files to the DOM.
How can I set up a queue system so that when I fire A1->A2->A3. I can guarantee that they are appeneded as A1->A2->A3 in that order.
For example, suppose A2 arrives before A1. I would want the action to wait upon the arrival and loading of A1.
One idea is to create a status checker using a timed callback as such
// pseudo-code
function check(ready, fund) {
// check ready some how
if (ready) {
func();
} else {
setTimeout(function () {
check(ready, fund);
}, 1); // check every msec
}
}
but this seems like a resource heavy way, as I fire the same function every 1msec, until the resources is loaded.
Is this the right path to complete this problem?
status checker using a 1msec-timed callback - but this seems like a resource heavy way; Is this the right path to complete this problem?
No. You should have a look at Promises. That way, you can easily formulate it like this:
var a1 = getPromiseForAjaxResult(ressource1url);
var a2 = getPromiseForAjaxResult(ressource2url);
var a3 = getPromiseForAjaxResult(ressource3url);
a1.then(function(res) {
append(res);
return a2;
}).then(function(res) {
append(res);
return a3;
}).then(append);
For example, jQuery's .ajax function implements this.
You can try something like this:
var resourceData = {};
var resourcesLoaded = 0;
function loadResource(resource, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var state = this.readyState;
var responseCode = request.status;
if(state == this.DONE && responseCode == 200) {
callback(resource, this.responseText);
}
};
xhr.open("get", resource, true);
xhr.send();
}
//Assuming that resources is an array of path names
function loadResources(resources) {
for(var i = 0; i < resources.length; i++) {
loadResource(resources[i], function(resource, responseText) {
//Store the data of the resource in to the resourceData map,
//using the resource name as the key. Then increment the
//resource counter.
resourceData[resource] = responseText;
resourcesLoaded++;
//If the number of resources that we have loaded is equal
//to the total number of resources, it means that we have
//all our resources.
if(resourcesLoaded === resources.length) {
//Manipulate the data in the order that you desire.
//Everything you need is inside resourceData, keyed
//by the resource url.
...
...
}
});
}
}
If certain components must be loaded and executed before (like certain JS files) others, you can queue up your AJAX requests like so:
function loadResource(resource, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var state = this.readyState;
var responseCode = request.status;
if(state == this.DONE && responseCode == 200) {
//Do whatever you need to do with this.responseText
...
...
callback();
}
};
xhr.open("get", resource, true);
xhr.send();
}
function run() {
var resources = [
"path/to/some/resource.html",
"path/to/some/other/resource.html",
...
"http://example.org/path/to/remote/resource.html"
];
//Function that sequentially loads the resources, so that the next resource
//will not be loaded until first one has finished loading. I accomplish
//this by calling the function itself in the callback to the loadResource
//function. This function is not truly recursive since the callback
//invocation (even though it is the function itself) is an independent call
//and therefore will not be part of the original callstack.
function load(i) {
if (i < resources.length) {
loadResource(resources[i], function () {
load(++i);
});
}
}
load(0);
}
This way, the next file will not be loaded until the previous one has finished loading.
If you cannot use any third-party libraries, you can use my solution. However, your life will probably be much easier if you do what Bergi suggested and use Promises.
There's no need to call check() every millisecond, just run it in the xhr's onreadystatechange. If you provide a bit more of your code, I can explain further.
I would have a queue of functions to execute and each of them checks the previous result has completed before executing.
var remoteResults[]
function requestRemoteResouse(index, fetchFunction) {
// the argument fetchFunction is a function that fetches the remote content
// once the content is ready it call the passed in function with the result.
fetchFunction(
function(result) {
// add the remote result to the list of results
remoteResults[index] = result
// write as many results as ready.
writeResultsWhenReady(index);
});
}
function writeResults(index) {
var i;
// Execute all functions at least once
for(i = 0; i < remoteResults.length; i++) {
if(!remoteResults[i]) {
return;
}
// Call the function that is the ith result
// This will modify the dom.
remoteResults[i]();
// Blank the result to ensure we don't double execute
// Store a function so we can do a simple boolean check.
remoteResults[i] = function(){};
}
}
requestRemoteResouse(0, [Function to fetch the first resouse]);
requestRemoteResouse(1, [Function to fetch the second resouse]);
requestRemoteResouse(2, [Function to fetch the thrid resouse]);
Please note that this is currently O(n^2) for simplicity, it would get faster but more complex if you stored an object at every index of remoteResults, which had a hasRendered property. Then you would only scan back until you found a result that had not yet occurred or one that has been rendered.