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);
}
}
}
);
}
Related
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
}
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 need a little help. I'm trying to run my second function "likeLinks();" but only after my first function "getLikeURLs();" is finished. This is because my 2nd function relies on the links Array to execute. It seems like they are trying to run at the same time.
Any help would be appreciated.
var links = [];
var url = '/' + window.location.pathname.split('/')[1] + '/' + window.location.pathname.split('/')[2] + '/'
getLikeURLs();
likeLinks();
function getLikeURLs() {
for (i = 1; i < parseInt(document.getElementsByClassName('PageNav')[0].getAttribute('data-last')) + 2; i++) {
var link = $.get(url + 'page-' + i, function(data) {
//gets the like links from current page
$(data).find('a[class="LikeLink item control like"]').each(function() {
links.push($(this).attr('href')); // Puts the links in the Array
});
});
}
}
function likeLinks() {
for (t = 0; t <= links.length; t++) {
var token = document.getElementsByName('_xfToken')[0].getAttribute('value')
$.post(links[t], {
_xfToken: token,
_xfNoRedirect: 1,
_xfResponseType: 'json'
}, function(data) {});
}
}
The link variables are actually jQuery deferred objects - store them in an array and then you can use $.when() to create a mew deferred object that only resolves when all of the previous $.get() operations have completed:
function getLikeURLs(url) { // NB: parameter, not global
var defs = [], links = []; // NB: links no longer global
for (...) {
var link = $.get(...);
defs.push(link);
}
// wait for previous `$.get` to finish, and when they have create a new
// deferred object that will return the entire array of links
return $.when.apply($, defs).then(function() { return links; });
}
Then, to start the chain of functions:
getLikeURLs(url).then(likeLinks);
Note that likeLinks will now be passed the array of links instead of accessing it from the global state. That function should also be rewritten to allow you to wait for its $.post calls to complete, too:
function likeLinks(links) {
// loop invariant - take it outside the loop
var token = document.getElementsByName('_xfToken')[0].getAttribute('value');
// create array of deferreds, one for each link
var defs = links.map(function(link) {
return $.post(link, {
_xfToken: token,
_xfNoRedirect: 1,
_xfResponseType: 'json'
});
});
// and another for when they're all done
return $.when.apply($, defs);
}
p.s. don't put that (relatively) expensive parseInt(document.getAttribute(...)) expression within the for statement - it'll cause it to be evaluated every iteration. Calculate it once outside the loop and store it in a variable. There's a few other places where you're repeating calls unnecessarily, e.g. window.location.pathname.split()
EDIT: My answer discusses the issue but see Alnitak answer for a much better solution.
The get in getLikeURLs and the put in likeLinks are both asynchronous. The calls to both of these function return immediately. When data is returned from the called server at some indeterminate time later, the callback functions are then called. The puts could return before the gets which would be a problem in your case. Also note that JavaScript is NOT multi-threaded so the two methods, getLikeURLs and likeLinks will never run at the same time. The callback functions, on the other hand, might be called at anytime later with no guarantee as to the call back order. For example, the 3rd get/put might return before the 1st get/put in your loops.
You could use $.ajax to specify that the gets and puts are synchronous but this is ill advised because the browser will hang if ANY get/put doesn't return in a reasonable amount of time (e.g. server is offline). Plus you don't have the "multi-tasking" benefit of sending out a lot of requests and having the various servers working at the same time. They would do so serially.
The trick is to simply call likeLinks form the callback function in getLikeURL. Your case is a little tricky because of the for loop but this should work:
var links = [];
var url = '/' + window.location.pathname.split('/')[1] + '/' + window.location.pathname.split('/')[2] + '/'
getLikeURLs();
//likeLinks(); // Don't call yet. Wait for gets to all return.
function getLikeURLs() {
var returnCount = 0; // Initialize a callback counter.
var count = parseInt(document.getElementsByClassName('PageNav')[0].getAttribute('data-last')) + 1;
for (i = 0; i < count; i++) {
var link = $.get(url + 'page-' + (i + 1), function(data) {
//gets the like links from current page
$(data).find('a[class="LikeLink item control like"]').each(function() {
links.push($(this).attr('href')); // Puts the links in the Array
});
// If all gets have returned, call likeLinks.
returnCount++;
if (returnCount === count) {
likeLinks();
}
});
}
}
function likeLinks() {
for (t = 0; t <= links.length; t++) {
var token = document.getElementsByName('_xfToken')[0].getAttribute('value')
$.post(links[t], {
_xfToken: token,
_xfNoRedirect: 1,
_xfResponseType: 'json'
}, function(data) {});
}
}
geturls(data,function(urls){
var data = {
"data": [
{ "userProfile": userP },
{ "urls": urls }
]
};
res.send(data);
});
function getUrls(data,done){
links = new Array();
for (var i=0; i<data.length; i++){
user = data[i]
Url.find({where:{data.id}}).success(function(url){
links.push({
"url": ur.text,
"date": data.syncedTime
});
if (urls.length == data.length){
done(links);
}
});
}
}
My problem with my code is this:
I'm returning the response through a callback once data collected in my array equals the length of the parent array. This is obviously a very dangerous and not so elegant solution. As, suppose I get a .failure from Url database, then my urls.length won't be equal with data.length. So, I'm a bit confused how to go about this.
Any help?
It will be easy for you, if you use async.js.
I used mapSeries here. It takes 3 parameters.
collection/array
iterator, which will be called for each item in the passed collection/array with 2 arguments. 1. item in collection, 2. callback. After completing the job in iterator, You should call the callback in node style(err first, results follows).
Final callback, which will be called after all the items in the collection mapped.
function getUrls(data,done){
var async = require('async');
async.mapSeries(data, function(user, cb) {//If you want it to be async `async.map`
Url.find({where:{user.id}}).success(function(url){
cb(null, {
"url": url.text,
"date": user.syncedTime
});
});
}, function(err, results) {
//results is an array. Its the same as `links` in your old code.
done(results);
});
}
geturls(data,function(urls){
var data = {
"data": [
{ "userProfile": userP },
{ "urls": urls }
]
};
res.send(data);
});
Use recursion:
function getUrls(data,done) {
var links = new Array();
function doGetUrl(i) {
var user = data[i];
Url.find({where:{data.id}}).
success(function(url){
links.push({
"url": ur.text,
"date": data.syncedTime
});
if (links.length == data.length){
done(links);
} else {
doGetUrl(i + 1); // get next url
}
}).
failure(function(err) {
doGetUrl(i); // on error, try to get current url again
// other error handling code
});
}
doGetUrl(0);
}
I would probably make use of the complete callback, in jQuery terms. Have a counter that records how many records have been processed and update this in complete, as this executes on success or failure. Then when that counter is >= the length of the data array you can exit.
As an aside, I would always do a >= rather than an == for the comparison you are doing there, that way, if for any crazy reason, the count is upped more than it should you still exit.
If alll you want to do is avoif the problem of checking links.length to determine when you are done then I think its just a matter of adding a separate counter that gets incremented even if the urk database fails. If you do that you can continue using your current stype where the async requests are run in parallel.
var nreq = 0;
for (var i=0; i<data.length; i++){
doTheAsyncOperation(function(){
//Run this part in both the success and error cases
nreq = nreq + 1;
if(nreq >= data.length){ done(links) }
})
}
On the other hand, if you want to run one query after the other you will need to rewrite the for to use recursion. This time, you don't need to worry about keeping a separate counter since you know when the final request runs:
function loop(i){
if(i >= data.length){
done(links);
}else{
doTheAsyncOperation(function(){
loop(i+1);
})
}
}
loop(0);
Finally, its good to know how to code this sort of patterns yourself but in the long run I highly recommend using a control flow library to keep things cleaner.
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.