Wait until enclosure's async method completes javascript - 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);

Related

Passing a variable between functions using jQuery

I am having trouble passing a variable from one function to another.
This code is from a PhoneGap app that I am working on, the idea is a QR code gets scanned using the offlineScan function which calls checkforOfflineTicket to check the local storage for ticket validation and returns the variable ticketCheck to determine whether the ticket is accepted.
My code below:
function checkforOfflineTicket(ticketID){
var ticketCheck = '1';
db = openDatabase(shortName, version, displayName,maxSize);
db.transaction(function(transaction) {
transaction.executeSql('SELECT * FROM tickets where ticketid=(?)', [ticketID],
function(transaction, result) {
if (result.rows.length == '1') {
if(result.rows.item(0)['status'] == '0'){
ticketCheck = 'OK';
}
else if(result.rows.item(0)['status'] == '1'){
ticketCheck = 'DUPLICATE';
}
else{
ticketCheck = 'ERROR';
}
}
else{
ticketCheck = 'NONE';
}
alert('the ticket check is '+ticketCheck);
},function(transaction, error) {
console.log("Error processing SQL: "+error.message);
});
},errorHandler,nullHandler);
return ticketCheck;
};
function offlineScan(){
cordova.plugins.barcodeScanner.scan(
function (result) {
if(!result.cancelled){
if(result.format == "QR_CODE"){
var ticketCheck = 'test';
var ticketID = result.text; // The ticketID is the full url
values=ticketID.split('='); // Split it at = to get the tickethash
one=values[0]; // url
two=values[1]; // hash
ticketCheck = checkforOfflineTicket(two);
alert('ticket check should be '+ ticketCheck);
} // End if result is QR
}
},
function (error) {
alert("Scanning failed: " + error);
}
);
}
The checkforOfflineTicket function is currently returning the alert the ticket check is OK and then second alert in the offlineScan function returns ticket check should be undefined. I have tried returning the variable in different places but no matter where I put it it does not get passed to the offlineScan function.
What am I missing? Thank you for any help!
This is caused by asynchronous method calls in your code. In checkforOfflineTicket the function to fetch the result set and doing the alert is called asynchronously to your offlineScan function. You have to chain your functions to get the correct order of execution. Following shows one possible way of chaining:
function checkforOfflineTicket(ticketID, callback) {
var ticketCheck = '1';
db = openDatabase(shortName, version, displayName,maxSize);
db.transaction(function(transaction) {
transaction.executeSql('SELECT * FROM tickets where ticketid=(?)', [ticketID],
function(transaction, result) {
...
alert('the ticket check is '+ticketCheck);
if (callback) callback();
}, ...
}
}
function offlineScan(){
cordova.plugins.barcodeScanner.scan(
function (result) {
if(!result.cancelled){
if(result.format == "QR_CODE"){
...
ticketCheck = checkforOfflineTicket(two, function() {
alert('ticket check should be '+ ticketCheck);
);
} // End if result is QR
}
},
function (error) {
alert("Scanning failed: " + error);
}
);
}

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

How can I control functions to execute in order?

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.

Angular foreach wants to wait until response for single call

I have thousands of records in my array. I wish to perform some function on each element of my array. Though my functions works fine, but I want to delay the call of next item until first is parsed properly. I don't have relevancy of return either it is true or not. I just wish to delay loop until single item is parsed.
I tried.
$scope.getInfo = function (funcName) {
$http.get(Youtube.getDetails(funcName)).success(function (data, status, headers, config) { // this callback will be called asynchronously when the response is available
if (!angular.isUndefinedOrNull(data.nextPageToken)) {
$scope.form.playlistToken = data.nextPageToken;
} else {
$scope.form.playlistToken = "";
$scope.form.playlistUrl = "";
}
if (data.items.length > 0) {
angular.forEach(data.items, function (value, key) {
var youTubeData = Youtube.parseVideoDetail(value);
if (!angular.isUndefinedOrNull(youTubeData.videoId)) {
Video.find({filter: {where: {videoId: youTubeData.videoId}}}).$promise.then(function (data) {
if (data.length == 0) {
Video.create(youTubeData).$promise.then(function (value, responseHeader) {
$scope.items.splice(0, 0, youTubeData);
youTubeData = {};
// $scope.form.url = "";
toastr.success("Record saved successfully", "Success");
}, function (reason) {
toastr.error("Video can't be saved. Please check.", "Error");
});
} else {
duplicate.push(youTubeData.videoId);
toastr.error("Video already exist with id - " + youTubeData.videoId, "Error");
}
});
}
});
} else {
failed.push(funcName.values.id);
toastr.warning("No details found !!", "Warning");
}
}).error(function (data, status, headers, config) { // called asynchronously if an error occurs or server returns response with an error status.
toastr.error("Youtube API failed to fetch data.", "Error");
});
}
$scope.saveVideo = function () {
// var videoId = Youtube.getYoutubeParser($scope.form.url);
var arrVid = []; // this is actually an array of 2K+ items.
angular.forEach(arrVid, function (value, key) {
var videoId = value.replace("?v=", "");
Youtube.params.videoInfo.values.id = videoId;
$scope.getInfo(Youtube.params.videoInfo);
});
console.log("Failed Videos" + JSON.stringify(failed));
console.log("Duplicate Videos" + JSON.stringify(duplicate));
}
don't use foreach for your scenario. you can utilize $timeout or $interval of angularjs. both of them perform start and stop operations with some interval.

How to simplify my javascript code(ajax)

I use ajax in my code. I have to call connectHost() from $('SyncDataPort0') to $('SyncDataPort5'),
function connectHost()
{
ajaxFrame($('SyncDataPort0').value, getConnectStatus);
}
function getConnectStatus(transport)
{
try {
rs = transport.responseText;
if(rs == 'OK') {
//$('SyncDataState0').innerHTML = 'ok';
addStateMsg($('ConnectTest'),getMsg('msgConnectOk'));
} else //NOT OK
addStateMsg($('ConnectTest'),getMsg('msgConnectNotOkResult').replace('%s',rs));
}catch(e){alert(e)};
}
function ajaxFrame(url, pars, onCompleteFun)
{
if (3 in arguments)
addStateMsg(arguments[3],getMsg('msgDataSending'),0);
new Ajax.Request(url,
{
method:'post',
parameters:pars,
onComplete: function(transport)
{
var rs = transport.responseText;
if('logout' == rs)
location.href='/index.php?menu=logout';
else if('' == rs)
{
//do nothing
}else
onCompleteFun.apply(this,[transport]);
},
onFailure:function()
{
debug('Load Data Failure!');
}
});
return true;
}
The question is how can i implement the function without reproducing the getConnectStatus
callback function???
If you use an inline function declaration, you can refer to variables in the parent scope and you can pass the port to your connectHost() function.
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
// you can refer to portNum here in the callback
try {
rs = transport.responseText;
if(rs == 'OK') {
//$('SyncDataState0').innerHTML = 'ok';
addStateMsg($('ConnectTest'),getMsg('msgConnectOk'));
} else //NOT OK
addStateMsg($('ConnectTest'),getMsg('msgConnectNotOkResult').replace('%s',rs));
} catch(e) {alert(e)};
});
}
If you want getConnectStatus() to still be its own function, then you can use an inline stub function like this:
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
getConnectStatus(transport, portNum);
});
}
And getConnectStatus() will have the portNum as the second argument. You can pass as many arguments through to the callback as you like this way.
If getConnectStatus() needs the value of this preserved, then you would do this:
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
getConnectStatus.call(this, transport, portNum);
});
}

Categories

Resources