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);
}
);
}
Related
I'm pretty new to JS since I've switched from C#.
My Question is how I can return mysql query values without problems.
chat.registerCmd('getid', (player, arg) => {
db.getPlayerPermission(player.socialId, function(permission){
PermissionValue = permission;
});
var vname;
var name;
if(arg.length <= 1) {
chat.send(player, '/getid [Vorname] [Name]');
return;
}
vname = arg[0];
name = arg[1];
db.getCharIdByName(vname, name, function(charid){
chat.send(player, 'Die ID des Spielers ist: ' + charid);
});
});
Is this a good way to return query values?
export function getPlayerPermission(socialid, callback){
connection.query('SELECT permission FROM permissions WHERE socialid=?',socialid, function(err, rows){
if(err){
console.log(err);
return;
}
callback(rows[0].permission);
});
}
You are using a callback-based approach which is completely fine. You can also use promises. With the callback apparoach, you can return both errors and the result of queries. The first argument can be the error while the second argument can be the returned value
export function getPlayerPermission(socialid, callback) {
connection.query('SELECT permission FROM permissions WHERE socialid=?',socialid,
function(err, rows){
if(err){
console.log(err);
callback(err);
return;
}
callback(null, rows[0].permission);
});
}
Now in your other file, you can call the function as follows
chat.registerCmd('getid', (player, arg) => {
db.getPlayerPermission(player.socialId, function(dbErr, permission) {
// since JS is asynchronous, you need to be in callback function to execute the rest of the code after you get the data from first query
if (dbErr) {
console.log(dbErr);
return;
}
// Now permission variable has data returned from the query
PermissionValue = permission;
var vname;
var name;
if(arg.length <= 1) {
chat.send(player, '/getid [Vorname] [Name]');
return;
}
vname = arg[0];
name = arg[1];
db.getCharIdByName(vname, name, function(charid){
chat.send(player, 'Die ID des Spielers ist: ' + charid);
});
});
});
I have a function (that contains promises internally so it itself runs synchronously) that seems to be running asynchronously within my main code. No matter how I format my promise it seems like the resolve gets sent before the functions ends execution:
This problem is also logically recursive, in that if I try adding another promise around the nameExists function (within this very promise) and then putting the resolve in a 'then', i just run into the same issue with the nested resolve...
document.getElementById("config-select").addEventListener("input", function(){
//check if the doc name exists: returns doc id
//promise that doc_obj is created before moving on
let doc_obj = {};
let promise = new Promise(function (resolve, reject) {
let doc_name = document.getElementById("config-select").value;
doc_obj = nameExists(doc_name);
resolve('done'); //this executes BEFORE nameExists is done processing...bringing back the original asynch issue i was trying to fix in the first place...
});
promise.then(function (result) {
alert("then: "+doc_obj);
if(doc_obj.bool === true){//it does exist:
alert("replacing id");
document.getElementById("config-select").setAttribute("doc-id", doc_obj.id);
}
else{//it doesn't:
alert("resetting id");
document.getElementById("config-select").setAttribute("doc-id", "");
}
}
);
});
The nameExists function:
//check if the name in config-select is an existing doc (assumes name is a unique document field)
const nameExists = function(name){
//get all docs
localDB.allDocs({include_docs: true}).then(function (result) {
//return object set to default state if no match is found
let doc_obj = {bool: false, id: ""};
alert("Entering the match checker...");
for(let i =0; i<result.total_rows; i++) {
if(result.rows[i].doc.name == name){
alert(result.rows[i].doc.name);
alert(name);
doc_obj.bool = true;
doc_obj.id = result.rows[i].doc._id;
//found a match
break;
}
}
//return the result
alert("returned obj.id: "+doc_obj.bool);
return doc_obj;
}).catch(function (err) {console.log(err);});
};
Ideally, I would like the doc_obj or some return value object to be populated with data from the nameExists function, before evaluating my 'if statements'. How can I format my promise/resolve statement to achieve this?
You should drop that new Promise - it doesn't change anything about whether you will be able to wait for nameExists' result or not. You will need to return the promise that then() creates inside the nameExists function:
function nameExists(name) {
return localDB.allDocs({include_docs: true}).then(function (result) {
//^^^^^^
for (let i =0; i<result.total_rows; i++) {
if (result.rows[i].doc.name == name){
return {bool: true, id: result.rows[i].doc._id};
}
}
return {bool: false, id: ""};
});
// ^ don't catch errors here if you cannot handle them and provide a fallback result
}
Then you can just wait for it in your event listener:
document.getElementById("config-select").addEventListener("input", function() {
const doc_select = document.getElementById("config-select");
const doc_name = doc_select.value;
// check if the doc name exists: returns doc id
nameExists(doc_name).then(function(doc_obj) {
//^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^
console.log("then", doc_obj);
if (doc_obj.bool) { // it does exist:
alert("replacing id");
} else { // it doesn't:
alert("resetting id");
}
doc_select.setAttribute("doc-id", doc_obj.id); // id is "" when it doesn't exist
}).catch(function (err) {
console.log(err);
})
});
the only async call you have is inside the nameExists function which is the database call so there is no need to write two promises, only one is enough to solve your issue.
the first event should be like that:
document.getElementById("config-select").addEventListener("input", function(){
nameExists(doc_name).then(function(doc_obj) {
alert("then: "+doc_obj);
if(doc_obj.bool === true){//it does exist:
alert("replacing id");
document.getElementById("config-select").setAttribute("doc-id", doc_obj.id);
}
else{//it doesn't:
alert("resetting id");
document.getElementById("config-select").setAttribute("doc-id", "");
}
}).catch(function (err) { console.log(err) });
});
and the nameExists function should look like that:
//check if the name in config-select is an existing doc (assumes name is a unique document field)
const nameExists = function(name){
//get all docs
return localDB.allDocs({include_docs: true}).then(function (result) {
//return object set to default state if no match is found
let doc_obj = {bool: false, id: ""};
alert("Entering the match checker...");
for(let i =0; i<result.total_rows; i++) {
if(result.rows[i].doc.name == name){
alert(result.rows[i].doc.name);
alert(name);
doc_obj.bool = true;
doc_obj.id = result.rows[i].doc._id;
//found a match
break;
}
}
//return the result
alert("returned obj.id: "+doc_obj.bool);
return(doc_obj); // here is where the code runs then statement inside the event
});
};
I trying to get the value (true or false) from nested functions inside of a method, but value returns UNDEFINED. How I can get a value from a object nested function?
var result = {
compute: function() {
this.retorno = result.transaction();
},
transaction: function() {
var db = window.sqlitePlugin.openDatabase({name: 'database.db', location: 'default'});
db.transaction(checaFR,erroFR); //Check if (F)irst (R)un.
function checaFR(tx){ //Check if table exists
tx.executeSql('SELECT * FROM setup',[],checaFRSuccess,erroFR2);
alert("Select Query OK");
}
function erroFR(err){ //Return if error
alert('Ops - '+err);
return false; //This value I Need! :(
}
function checaFRSuccess(tx,result){
alert("Query Sucess "+result.rows.length);
return ('Rows: '+result.rows.length);
}
function erroFR2(err2) { //If no DB table
alert("erroFR2: "+JSON.stringify(err2));
db.transaction(populateDB, errorCB, successCB); //Start DB Populate
}
function populateDB(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS setup (id INTEGER PRIMARY KEY AUTOINCREMENT, runned TEXT NOT NULL)');
tx.executeSql('INSERT INTO setup(runned) VALUES ("1")');
}
function errorCB(errCB) {
alert("Error processing SQL: "+errCB);
return false; //This value I Need! :(
}
function successCB() {
alert("successCB OK");
return true; //This value I Need! :(
}
}
};
result.compute(); //Start the main function
return {
retorno: "var: "+result.retorno //This returns UNDEFINED
};
How I can get the value from this function and pass it?
Note: All callbacks and alerts are working, including the database creation. :D
You can refer to the function nested inside using the following code.
result.transaction().erroFR();
result.transaction().errorCB();
But before that you need to return the Object from the inner function which needs to be publicly accessible. This is basically a module pattern, where you can expose only the relevant information to outside world. So you need to expose the functions as shown below. Note the return statement at the end of the function which is returning an object.
var result = {
compute: function() {
this.retorno = result.transaction();
},
transaction: function() {
var db = window.sqlitePlugin.openDatabase({name: 'database.db', location: 'default'});
db.transaction(checaFR,erroFR); //Check if (F)irst (R)un.
function checaFR(tx){ //Check if table exists
tx.executeSql('SELECT * FROM setup',[],checaFRSuccess,erroFR2);
alert("Select Query OK");
}
function erroFR(err){ //Return if error
alert('Ops - '+err);
return false; //This value I Need! :(
}
function checaFRSuccess(tx,result){
alert("Query Sucess "+result.rows.length);
return ('Rows: '+result.rows.length);
}
function erroFR2(err2) { //If no DB table
alert("erroFR2: "+JSON.stringify(err2));
db.transaction(populateDB, errorCB, successCB); //Start DB Populate
}
function populateDB(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS setup (id INTEGER PRIMARY KEY AUTOINCREMENT, runned TEXT NOT NULL)');
tx.executeSql('INSERT INTO setup(runned) VALUES ("1")');
}
function errorCB(errCB) {
alert("Error processing SQL: "+errCB);
return false; //This value I Need! :(
}
function successCB() {
alert("successCB OK");
return true; //This value I Need! :(
}
// Return an object containing functions which needs to be exposed publicly
return {
checaFR : checaFR,
erroFR : erroFR,
checaFRSuccess : checaFRSuccess,
erroFR2 : erroFR2,
populateDB : populateDB,
errorCB : errorCB,
successCB : successCB
}
}
};
You need to pass a callback function into the result.transaction method, and the false value would be passed into that callback function.
You have to do this bc false is derived during/after db.transaction, which is an asynchronous method... so we have to pass false asynchronously as well.
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.
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);