How can I control functions to execute in order? - javascript

I am newbie in Javascript. I am trying to manage couple of function in order. But when it gets to API calls it dosen't wait for response and go backs to continue its execution and makes my code messy. This is a sample:
function readFacebook()
{
var myID = getMyID();
console.log("myID= " + myID);
}
function getMyID(){
FB.api('/me', function(response) {
console.log("response.id= "+response.id);
return(response.id);
});
}
The output is completely strange. First
console.log("myID= " + myID);
show output and then
console.log("response.id= "+response.id);
will be called. Anyone can explain how I can force it to implement in order. I meant program should wait until response from facebook instead of working asynchronously!
EDIT
I actually call 3 time API from main function to three sub functions. How can I organise this:
function getMyID(){
FB.api('/me', function(response) {
console.log("response.id= "+response.id);
return(response.id);
});
}
function readFacebookEvent(id)
{
var myID = getMyID();
console.log("myID= " + myID);
FB.api('/me/events', function(response) {
for(i=0; i<response.data.length;i++) {
var str;
var eventID = response.data[i].id;
getEvent(eventID,myID);
}
});
}
function getEvent(eventID,myID){
FB.api("/"+ eventID , function (response3) {
if (response3 && !response3.error) {
//console.log(response3);
var date = new Date((response3.start_time || "").replace(/-/g,"/").replace(/[TZ]/g," "));
var diff = (((new Date()).getTime() - date.getTime()) / 1000);
//console.log(diff);
if(myID == response3.owner.id && diff < 0 )
{
//console.log("found= " + myID);
var t = getImage(eventID);
if(t)
{
console.log("TRUE");
}
else
{
console.log("false");
}
}
}
});
}
function getImage(eventID){
//console.log("******eventID== "+eventID);
FB.api("/"+eventID+"/picture",
{
"redirect": false,
"type": "normal"
},function (response2) {
if (response2 && !response2.error) {
str="<br/><b>Pic</b> : <img src='"+response2.data.url+"'/>";
//console.log("response2.data.url= "+response2.data.url);
//str +="<b>name: </b>"+response3.name+"<br>";
document.getElementById("status2").innerHTML+=str;
return true;
}
else
{
return false;
}
});
}

As you mentioned the call to facebook's api is asynchronous meaning that you cannot be certain to know when the resulting data will come back from your request. You can work around and guarantee the order you're looking for by using a callback:
function readFacebook(id) {
console.log("myID= " + id);
}
function getMyID(cb) {
FB.api('/me', function(response) {
console.log("response.id= "+response.id);
cb(response.id);
});
}
getMyID(readFacebook);
What's happening here is that the call to FB.api accepts a callback that fires when the response comes back from the server. Since we're supplying our own callback to getMyID, we can use that to get access to response.id once the server responds.

Related

More JQuery/Ajax and when/done/promise confusion

Once again I struggle with ajax calls - this time around some chaining issue. Overall here is what I need to accomplish:
I loop over some array, and for each item in the array, I need to do the following:
Issue an Ajax call, and upon success, I need to issue three other calls, which must be chained, so they run in sequence.
When all the items in the array have both their main call and the three chained subcalls completed, I must be able to do some action.
My problem is, that the program does not wait for the three chained subcalls to complete. In the code below, this can be seen by the "Done" statement in the log turns up before the subcalls have completed.
I have created a JSFiddle here: https://jsfiddle.net/LeifFrederiksen/td534phz/1/
Note: I have two different function for the addAttachments function (addAttachments and addAttachmentsAlternative) - none of them works like they should.
var items = ["A","B"];
save();
function doneSaving() {
log("<H1>Done</H1>");
}
function save() {
// Save all items, and do something when all is done...
log("<H1>Save initiated</H1>");
var returnValue = saveItems();
$.when(returnValue).done(function() {
doneSaving();
})
}
function saveItems() {
// Loop through all items and save each of them...
var requests = Array();
// Build array of requests to wait for...
for (item of items) {
requests.push(saveOneItem(item));
}
var returnValue = $.when.apply($, requests).done(function() {
log("All requests completed");
})
return returnValue;
}
function saveOneItem(item) {
// Save one item...
return addListItem(item,addListItemSuccess,addListItemFailure);
}
function addListItem(item, successFunction, failureFunction) {
// The actual ajax that handles saving to database (actually Sharepoint via REST)...
log("addListItem on: " + item);
var returnValue =
$.ajax({
url: "/echo/json/",
data: {html: item,
delay: 1},
}).done(function (data) {
if (successFunction != undefined) {
returnValue = successFunction(item, data); // Returns the newly created list item information
return returnValue;
}
}).fail(function (data) {
return failureFunction(item, data);
});
return returnValue;
}
function addListItemSuccess(item,data) {
log("addListItem succces - in succes function for " + item);
returnValue = addAttachmentsAlternative(item,data);
return returnValue;
}
function addAttachments(item,data) {
var attachment1Deferred = addListItem(item + "-attachment 1",addAttachmentSuccess,addAttachmentFailure);
var attachment2Deferred = attachment1Deferred.then(
function() {
return addListItem(item + "-attachment 2",addAttachmentSuccess,addAttachmentFailure);
});
var attachment3Deferred = attachment2Deferred.then(
function() {
return addListItem(item + "-attachment 3",addAttachmentSuccess,addAttachmentFailure);
});
attachment3Deferred.done(
function() {
log("Completed upload of all attachments for " + item);
})
return attachment3Deferred;
}
function addAttachmentsAlternative(item,data) {
return addListItem(item + "-attachment 1",addAttachmentSuccess,addAttachmentFailure)
.done(function(data) {
return addListItem(item + "-attachment 2",addAttachmentSuccess,addAttachmentFailure)
}).done(function(data) {
return addListItem(item + "-attachment 3",addAttachmentSuccess,addAttachmentFailure)
}).done(function(data) {
log("Completed alternative upload of all attachments for " + item);
});
}
function addAttachmentSuccess(item,data) {
log("addAttachment succces - in succes function for " + item);
var deferred = $.Deferred();
deferred.resolve();
return deferred;
}
function addListItemFailure(item,data) {
console.log("addListItem failed - calling failure function for " + item);
$("#console").append("<P>addListItem failed - in failure function for " + item);
}
function addAttachmentFailure(item,data) {
console.log("addListItem failed - calling failure function for " + item);
$("#console").append("<P>addListItem failed - in failure function for " + item);
}
function log(message) {
console.log(message);
$("#console").append("<P>" + message);
}
I am hoping to achieve some generic pattern that I can use in different cases.
I got my inspiration from this great article, but cannot get it to work in my scenario: https://medium.com/coding-design/writing-better-ajax-8ee4a7fb95f#.tu0sruz5k
Any ideas and inputs are more than welcome.
Regards
Leif
There are several issues with the provided example:
to chain the tasks of creating list items and adding attachments use
.then instead of .done. With .done callback that prints All requests completed it is fired once deferred (first ajax call in addListItem function) is getting resolved.
some functions like addListItem still uses callback function syntax, i would suggest convert them to promises
since all deferred are getting resolved in saveItems function there is no need to use jQuery.when() in save function
Modified demo

Function in while loop executes only once

I am a beginner in javascript, and I'm trying to figure out why my while loop won't actually loop more than once, even though the condition is always met.
I have a function sending an API request:
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true;
}
else {
console.log('False');
return false;
}
}).error(function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
I want to call this function until it returns true, so I call it this way:
var count = 0;
while (get_status(id, count) === false) {
count += 1;
}
The count variable is just added to see how many times it loops, it stays at 0 even though 'False' is displayed in the console.
Is there some behaviour I am misunderstanding here?
EDIT I understand why this won't work. My intention here is to display an iframe as long as the transaction status is pending. I thought of continually sending a request until the transaction status is something other then 'Pending', but I am aware there are more optimal ways.
Your get_status() function does not return a value. Thus, it's return value is undefined which is falsey so your while() loop stops after the very first iteration.
The return statements you do have in your code are inside of callbacks and have nothing to do with the return value of get_status().
What you are attempting to do is generally not a good design. It appears that you want to run a given Ajax call over and over with no delay until you get the answer you want. This will potentially hammer the destination server.
If you describe the problem you're really trying to solve, we could help come up with a better way to do this. Worst case, you could poll the server with a time delay between requests.
If you wanted to poll every so often, you could do something like this:
function get_status(trid, count) {
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
return $http(req).then(function(data) {
return data.transaction_status;
});
}
function poll_status(callback) {
function next() {
get_status(...).then(function(status) {
if (status === "Pending") {
// poll once every two seconds
setTimeout(next, 2000);
} else {
// status is no longer pending, so call the callback and pass it the status
callback(status);
}
}, function(err) {
callback(err);
});
}
next();
}
poll_status(function(result) {
// done polling here, status no longer Pending
});
This is not the correct way to deals with async calls, I'd create a recursive function which will call itself. (in this case get_status should return a promise)
Code
var count = 0, id = 1;//id should be some value
(function myCall(promise){}
promise.then(function(data){
count += 1;
if(data)
myCall(get_status(id, count)); //call function on conditon
});
}(get_status(id, count))
Method(Returning Promise)
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
//returning promise here
return $http(req).then(function(response) {
var data = response.data;
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true; //resolves the promise
}
else {
console.log('False');
return false; //resolves the promise
}
}, function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
You're trying to return from within an asynchronous callback, which won't work, unfortunately. Instead you'll want a module like async, specifically whilst.
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your code here, setting outcome instead of returning
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
outcome = true;
callback();
}
else {
outcome = false
callback();
}
}).error(function(data) {
outcome = true;
callback();
})
},
function (err) {
// All done!
}
);
But really the behavior you're looking for is probably checking on a status at pre-defined intervals. In this case, adapting the code
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your request stuff.
setTimeout(function () {
callback();
}, 1000); // Waits one second to begin next request
},
function (err) {
// All done!
}
);

Ajax call inside of loop needs to be synchronized

I have a coding issue where I want to loop thru and call an ajax call but I dont want another request to be sent until the first one is complete. I have tried setting it to asyc = false and adding an onsuccess callback. But it seems like the loop is continuing to run which gives me responses out of order and parallel requests.
// This function is used to generate a numeric val and passes it along in the success callback
function duplicateOmsid(totalAmount, omsid) {
var url = '/portal/GetBulkCopyAmountServlet';
var errorString;
new Ajax.Request(
url, {
method: 'post',
parameters: {
totalAmount: totalAmount,
omsid: omsid
},
async: false,
onSuccess: function(transport) {
dataResponse = transport.responseText.evalJSON();
createWorkflow(totalAmount, omsid, dataResponse);
},
.....
// Function used to loop thru and call the duplicate workflow ajax call
function createWorkflow(totalAmount, omsid, bulkAmount) {
var amountProccessed = 0;
for( i = 0; amountProccessed < totalAmount; i++ ) { // Loop through source
var duplicateAmt;
if (totalAmount < 11){
duplicateAmt = totalAmount
}else{
duplicateAmt = amountProccessed + dataResponse < totalAmount ? dataResponse : totalAmount - amountProccessed
}
duplicateWorkflow(totalAmount, omsid, duplicateAmt, amountProccessed);
amountProccessed += bulkAmount;
}
}
// Function used to create the workflow ajax call - the success handler is updating the user.
function duplicateWorkflow( totalAmount, omsid, bulkAmount, amountProccessed){
amountProccessed += bulkAmount;
var url = '/portal/CreateWorkFlowServlet';
var errorString;
new Ajax.Request(
url, {
method: 'post',
parameters: {
totalAmount: totalAmount,
omsid: omsid,
bulkAmount: bulkAmount
},
async: false,
onSuccess: function(transport) {
var div = document.getElementById('progress');
if( amountProccessed > totalAmount){
div.innerHTML = totalAmount + ' out of ' + totalAmount + ' Processed ' ;
alert (totalAmount + 'Items successfully duplicated ')
}else{
div.innerHTML = amountProccessed + ' out of ' + totalAmount + ' Processed ' ;
}
},
onFailure: function(e) {
}
},
onException: function(e) {
}
},
});
}
As a rule of thumb, the way to sequentialize async code using raw Javascript is to use recursion instead of a for loop.
var urls = [ /*...*/ ];
function loop(i, onDone){
if(i >= urls.length){
//base case
onDone( theResultOfProcessingTheAjaxRequests );
}else{
Ajax.Request(urls[i], {
onsuccess: function(){
loop(i+1, onDone);
}
});
}
}
loop(0, function(result){
console.log("all done");
});
Note that I converted i to a function parameter, to keep it scoped to the looping function. If you wanted, you could declare it outside, just like you did in the for loop:
var urls = [ /*...*/ ];
var i = 0;
function loop(onDone){
//...
i = i+1;
loop(onDone);
}
Additionally, I added an "onDone" callback to the looping function to help the async code look a bit more like the sync version. The idea is that by using a return callback, the loop function doesn't need to know what function called it and where it should jump to after its done its job - in the end, calling onDone(x) is a bit similar to doing return x. Of course, you could have hardcoded the return function if you wanted.
function afterAjax(){
console.log("all done");
}
function loop(){
if(i >= urls.length){
afterAjax();
}
//...
}
loop();
Finally, coding recursive loops like this is a bit annoying and there are many libraries out there that provide functions to encapsulate these hight level sequentialization and parallelization patterns. In particular, error handling (try-catch) is specially hard to do by hand with callbacks. If you are doing any more non-tricial Async stuff I would highly recommend looking into some of these libraries.

How to pass a parameter to a function in casperjs?

I have stored all the images url in an array, and am trying to test whether image has loaded properly. If you see the below code, i had to repeat few set of lines again and again. How can i write it to be generic?
casper.start()
var imagesArray = [];
imagesArray = ['https://www.google.co.in/images/srpr/logo11w.png',
'https://www.google.co.in/images/srpr/logo1w.png'];
casper.thenOpen(imagesArray[0], function () {
if (this.currentHTTPStatus === 404) {
this.warn(imagesArray[0] + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(imagesArray[0] + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
});
casper.thenOpen(imagesArray[1], function () {
if (this.currentHTTPStatus === 404) {
this.warn(imagesArray[0] + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(imagesArray[0] + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
});
casper.run(function() {
this.echo('Image loading test finished');
this.exit();
});
I tried the below method, calling a function but its throwing parser error, what am i doing wrong, or how can i proceed with it?
function checkImages(item){
if (this.currentHTTPStatus === 404) {
this.warn(item + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(item + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
}
casper.thenOpen(imagesArray[0], function () {
this.evaluate(checkImages(imagesArray[0]));
});
casper.thenOpen(imagesArray[1], function () {
this.evaluate(checkImages(imagesArray[1]));
});
Thanks in advance.
Since all then* function are asynchronous step function that insert a step into the queue, you can call them in a loop. Since imagesArray is a native array, it can be iterated over by using Array.prototype.forEach which PhantomJS supports:
var imagesArray = [
'https://www.google.co.in/images/srpr/logo11w.png',
'https://www.google.co.in/images/srpr/logo1w.png'
];
casper.start();
imagesArray.forEach(function(imageUrl){
casper.thenOpen(imageUrl, function () {
if (this.currentHTTPStatus === 404) {
this.warn(imageUrl + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(imageUrl + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
});
});
casper.run();
A simple for loop would have sufficed, but then you would have a problem with the imagesArray[i] inside of thenOpen. The i variable would never change, because every step is executed after the loop finished executing. So every imagesArray[i] would show the last url. Because JavaScript has function level scope the url bound to each iteration and never changes afterwards.
As a reminder, think of the evaluate() method as a gate between the CasperJS environment and the one of the page you have opened; everytime you pass a closure to evaluate(), you’re entering the page and execute code as if you were using the browser console. So you can't use evaluate on checkImages.
Use echo like this :
casper.thenOpen(imagesArray[0], function () {
this.echo(checkImages(imagesArray[0]));
});
casper.thenOpen(imagesArray[1], function () {
this.echo(checkImages(imagesArray[1]));
});
You don't have any need for thenOpen in this test case, since you only want to verify response code. you CAN do it that way, but it's incredibly wasteful of time/resources. this is how I achieved the same goal:
casper.test.begin('link tester', 73, function(test) {
casper.start(url);
getLinks = function(){
links = this.evaluate(function(){
var links = document.getElementsByTagName('a');
links = Array.prototype.map.call(links,function(link){
return link.getAttribute('href');
});
return links;
});
}
casper.then(getLinks);
casper.then(function(response) {
for (q = 0; q < links.length; q++) {
if (response == undefined || response.status >= 400) {
this.test.fail("URL " + links[q] + " failed with response code " + (response.status));
} else {
this.test.pass((response.status + " ---- " + links[q]));
}
}
});
the only caveat with this would be that you can only have 1 failure per casper function. if you are testing 100 URLs on a page, and you fail on the 4th one, you will have to fix that one before you can see if the others failed. thus, you would need to nest a casper.then() within your if statement. In case you were wondering, it would look like this:
casper.then(function(response) {
links.forEach(function(link){
if (response == undefined || response.status >= 400) {
casper.then(function() {
this.test.fail("URL " + link + " failed with response code " + (response.status));
})
} else {
this.test.pass((response.status + " ---- " + link));
}
});
});

Wait until enclosure's async method completes javascript

I am new to javascript programming. I just can't find an answer that works.
The problem is that my function only works when it is wrapped in setTimeout call like so:
var sPageIdentifier = 'ReportViewer';
UserPreferencesManager.Initialize(sPageIdentifier);
setTimeout(function () {
var strUserPrefs = UserPreferencesManager.GetPreferences();
console.log(strUserPrefs);
initLayout(strUserPrefs);
}, 1000);
function initLayout(strUserPrefs) {
//do stuff using strUserPrefs
}
If I comment out setTimeout function, the initLayout(strUserPrefs) fails because strUserPrefs is null.
Any help will be appreciated!
Here is the UserPreferencesManager.js code:
var UserPreferencesManager = function () {
var strPrefsID = null;
var strPrefsString = null;
return {
Initialize: function (strPrefsIDIn) {
strPrefsID = strPrefsIDIn;
strPrefsString = this.GetPreferences();
},
GetPreferences: function () {
if (!strPrefsID) {
alert("Validation Failed: the UserPreferencesManager must be initialized prior to usage.");
return null;
}
if (!strPrefsString) {
this.LoadPreferences();
return strPrefsString;
}
return strPrefsString;
},
LoadPreferences: function () {
if (!strPrefsID) {
alert("Validation Failed: the UserPreferencesManager must be initialized prior to usage.");
return null;
}
myasyncfunctioncall({
parameters: ["USR_PersonId", "abc", 'GET']
script_name: 'MAINTAIN_USER_PREFS',
onexception: function (exception, xhr, options) {
alert('Error: ' + xhr.statusText + +exception);
console.log(exception);
},
onsuccess: function (data, xhr, options) {
if (data == "User ID is zero") {
alert('MP_MAINTAIN_USER_PREFS: must be > 0.0');
strPrefsString = data;
}
else {
strPrefsString = data;
}
}
});
},// end of LoadPreferences
WritePreferences: function (strPrefsIn, strPrefsID) {
if (strPrefsID && typeof strPrefsID === "string") {
if (strPrefsIn != null) {
myasyncfunctioncall({
parameters: ["USR_PersonId", strPrefsID, strPrefsIn , 'SET']
script_name: 'MAINTAIN_USER_PREFS',
onexception: function (exception, xhr, options) {
alert('Error: ' + xhr.statusText + +exception);
console.log(exception);
},
onsuccess: function (data, xhr, options) {
if (data == "transaction-ok") {
UserPreferencesManager.LoadPreferences();
} else if (data == "User ID is zero") {
alert('MP_MAINTAIN_USER_PREFS: must be > 0.0');
}
}
});
} else {
alert("Error: Preferences object must be initialized prior to writing preferences");
}
} else {
alert('Error: The preference ID can\'t be null and must to be of type string');
return;
}
}// end of WritePreferences
};// end of return API
}(); // end of UserPreferencesManager
Seens like this myasyncfunctioncall is sending an async request. You'll need to add some variable to set if the response of this async request has arrived, and then, when it is set you can continue with your routine.
WHenever an async call is made on javascript, the program continues as if it was already completed. You have to mannually add checks to see if it has completed or not.
UserPreferencesManager.GetPreferences() is Making the asynchronous AJAX call to get the user preferences. So, in this case Javascript thread will continue execution in current thread context and executes initLayout(strUserPrefs). But at this state GetPreferences() call is still not complete and strUserPrefs is null.
SetTimeout is one of the trick to overcome this issue, which you did. But you can also design the APIs in such a way that it allows the callback function execution for each asynchronous AJAX calls.
Thanks for the tip, Balachandra!
Here is what I did, added two parameters to LoadPreferences method - callback and strPrefsID, so I can invoke fnCallback function within on success and pass it ajax data:
LoadPreferences: function (fnCallback, strPrefsID) {
if (!strPrefsID) {
alert("Validation Failed: the BhsUserPreferencesManager must be initialized prior to usage.");
return null;
}
if (strPrefsString) {
// strPrefsString is not null, so return it
callback(strPrefsString);
} else {
myasyncfunctioncall({
parameters: ["USR_PersonId", "abc", 'GET']
script_name: 'MAINTAIN_USER_PREFS',
onexception: function (exception, xhr, options) {
alert('Error: ' + xhr.statusText + +exception);
console.log(exception);
},
onsuccess: function (data, xhr, options) {
if (data == "User ID is zero") {
alert('MAINTAIN_USER_PREFS: must be > 0.0');
strPrefsString = data;
} else if (data.substring(0, 5) === "ERROR") {
alert(data);
} else {
fnCallback(data);
}
}
});
}
}// end of LoadPreferences
And here is how now I can call initLayout:
BhsUserPreferencesManager.LoadPreferences(initLayout, sPageIdentifier);

Categories

Resources