How to return this value? - javascript

I am building a mobile app using phonegap, jQuery and jQuery mobile. I want to use SqLite database in my app for storing some user information. (I can't use local storage i want to do search/sort operations on the data)
This is the code I am working on to get this done,
function getAccountInformation(){
var accounts = {};
db.transaction(function(transaction) {
transaction.executeSql('SELECT * FROM account;', [],
function(transaction, result) {
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
var item={};
var row = result.rows.item(i);
for(var prop in row){
item[prop]=row[prop]
}
accounts[i]=item;
}
}
},errorHandler
);
},errorHandler,nullHandler);
console.log(JSON.stringify(accounts));
}
If I put this console.log(JSON.stringify(accounts)); after the end } of the for loop it shows proper output.
But if I put it where it is right now the {} is printed as an output.
How can I make getAccountInformation() function return that accounts object to my other function? Where I will use jQuery to render the output.
What I want to do is return this accounts object simply by wrting
return accounts;

Because the SqLite functions are asynchronous you cannot just return the value.
I would make the getAccountInformation receiving a callback as below:
function getAccountInformation(callbackfn)
{
db.transaction(function(transaction),
.....,
function (transaction, result)
{
if (result != null)
{
callbackfn(result);
}
});
}
In such way you will get your function called when the db request executed.

That depends on when the function is called. When it is called asynchronously (like an AJAX request) you're out of luck. In that case I suggest you read about jQuery deferreds.
A code snippet based on deferreds could look like this:
var deferred = new jQuery.Deferred();
var accounts = {};
deferred.done(function(data) {
// process your data here
});
db.transaction(function(transaction) {
transaction.executeSql('SELECT * FROM account;', [],
function(transaction, result) {
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
var item={};
var row = result.rows.item(i);
for(var prop in row){
item[prop]=row[prop]
}
accounts[i]=item;
}
deferred.resolve(accounts); // let the "done" function get called
} else {
deferred.reject();
}
}, errorHandler
);
},errorHandler,nullHandler);

db.transaction and transaction.executeSql produce their results asynchronously through the use of callback functions. This means that your getAccountInformation function will return immediately and will not wait for the transaction to complete (as in a synchronous call).
It's probably easier to simply pass in a callback function as an argument of your getAccountInformation and run that function when the accounts array is populated. Therefore, change your function signature to getAccountInformation(callback) and replace the executeSql callback with:
function(transaction, result) {
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
var item={};
var row = result.rows.item(i);
for(var prop in row){
item[prop]=row[prop]
}
accounts[i]=item;
}
callback(accounts); // Call callback function with populated accounts
}
}
You can then call this function with:
getAccountInformation(function(accounts) {
// Do something with the accounts
});
There are fancier ways such as jQuery Deferreds and Promises which makes working with asynchronous functions easier, but you still need to understand why this exists. By using asynchronous actions, your application stays responsive while waiting for results.

Related

Run function right after asynchronous one

I want to store in chrome.storage ids. Using INSERT_OR_REMOVE("blocked",12) I am adding or removing 12 from database. The thing is that it is working asynchronous. What I want to do is to use colorize function right after. What should I do to archive that?
function INSERT_OR_REMOVE(table, id) {
if (isNaN(id))
return;
var parsed = parseInt(id);
chrome.storage.local.get(table, function (data) {
if (data[table] == null)
data[table] = [];
if (!data[table].includes(id))
data[table].push(id);
else
data[table].remByVal(id);
chrome.storage.local.set(data);
});
}
function colorize() {
chrome.storage.local.get("blocked", function (data) {
var buttons = $("div.base-btn").closest("table");
for (i = 0; i < buttons.length; i++) {
var id = $(buttons[i]).attr("data-id");
if (data["blocked"].includes(id))
$(buttons[i]).parent().addClass('blocked');
else
$(buttons[i]).parent().removeClass('blocked');
}
});
}
Chrome StorageArea.set() accepts a callback function which is run on success/failure. It is identical to the callback you passed to chrome.storage.local.get(table, callbackFunction)
You just need to add colorize as the callback to be run on completing set:
chrome.storage.local.set(data, colorize)

Call same promise within itself

Still trying to wrap my head around promises and how they work. Im querying the Google webmaster API to return Search Analytics data. I've set up a promise which returns the data if i call it once however i need to call it again based on the result of the previous.
For example:
startRow = 0;
data = [];
Query(startRow).then((results) => {
if (results != null) {
data.push(results)
startRow++;
// RUN SAME QUERY AGAIN
};
});
startRow needs to increase by 1 then call the same promise (with the updated startRow) if the promise returned data. Is this possible or am i looking at this totally the wrong way?
You can't call the same Promise more than once, only create new ones.
startRow = 0;
data = [];
function startQuery() {
// Generally a good idea to always return Promises,
// so you can chain them if needed
return Query(startRow).then(processResults);
}
function processResults(results) {
if (results == null) return;
data.push(results);
startRow++;
return startQuery();
};
startQuery();
Or, in a more compact way:
startRow = 0;
data = [];
function startQuery() {
return Query(startRow).then((results) => {
if (results == null) return;
data.push(results);
startRow++;
return startQuery();
});
}
startQuery();
What you can do is create a function that recursively returns all of the results from a certain page onward, then call that with an initial page value of 0:
function queryPaged(pageNum, soFar) {
return Query(pageNum).then(function (results) {
return results
? queryPaged(pageNum + 1, soFar.concat(results))
: soFar;
});
}
queryPaged(0, []).then(function (allResults) {
console.log(allResults);
});

Pass different versions of variable through asynchronous callback functions

I'm having trouble with some asynchronous database queries. I'm using the Async library for Node.js. I have some code forming an object before it hits the add() function. In my code, I have a loop the executes to create the object multiple times.
for(var i = 0; i < 10; i++){
var obj.id = i + '_idstring';
add(obj);
}
As you can see, each object is slight different. I need to perform a lookup in my database to see if the object already exists (1st waterfall function) and if it does not, add it to my database (2nd waterfall function). My issue is, loaObj.id always equals 10_idstring. Is this a problem with how I'm passing the object through the callbacks? How should I be handling this?
function add(loaObj) {
async.waterfall([
function(loaObj, callback) {
var sql = "";
connection.query(sql, function(error, results, fields) {
callback(error, results, loaObj);
});
},
function(results, loaObj, callback) {
if (results.length > 0) {
sendMessage();
} else {
var sql = "";
connection.query(sql, function(error, results, fields) {
callback(error, loaObj);
});
}
}
], function(err, loaObj) {
console.log(err);
if (err) {
sendMessage();
} else {
sendMessage();
}
});
}
Because you are using Object, which will be passed by "copy of a reference", then obj.id will be overridden with every loop and it will settle down to 10_idstring -where the loop stops-.
One simple solution is to pass new Object every time like below:
for(var i = 0; i < 10; i++){
add({id: `${i}_idstring`});
}

In calling URLs iteratively using http.get() and resolving using $q.all()

I am implementing this scenario where I have to fetch data from multiple URLs iteratively and process it with some business logic and display on screen. I am implementing this in the controller as it is a requirement. All is well until part-1 and I am getting the 6 promise objects in the promises array. But, I am not getting the data into metricData. I am seeing a null in the console while running in the browser. I am sure that the data is coming in the URL response. I feel I am doing something silly in the $q.all method. Is this correct?
var calculateMutationsInDepth = function(){
//Part-1
var promises=[];
var metricData=[];
for(var depth=0 ; depth<6 ; depth++){
var resourceUrl = urlService(depth);
promises.push($http.get(resourceUrl)
.then(function(response){
return response.data;
},function(status){
return status;
}));
}
//Part-2 Resolving the promise array below
$q.all(promises).then(function(data){
for(var eachResult=0; eachResult < data.length; eachResult++){
if(null != data[eachResult]){
var eachDataObject = data[eachResult];
//For debugging console.log(eachDataObject);
for(var objCount=0; objCount < eachDataObject.length; objCount++){
if(eachDataObject[objCount].scope === "PRJ" || eachDataObject[objCount].scope === "FIL")
metricData.push(eachDataObject[objCount]);
}
}
}
});
if(metricData != null){
analyzeMutationData(metricData); //Calling a function with the aggregated data array where business logic is present
}
};
calculateMutationsInDepth(); //Calling the above function
Yes, something silly.
As written, analyzeMutationData(metricData) is called synchronously whereas metricData is populated asynchronously inside the $q.all(promises).then() callback.
Also, as written the error handler function(status){ return status; } is inappropriate. Either :
omit the error handler entirely and allow any single $http error to prevent further processing in Part 2, or
return null, allowing processing in Part 2, and the if(dataObject != null) test in part 2 to filter out any such error.
Here's the revised code with a few other tidies and a demonstration of what can be done if calculateMutationsInDepth() returns a promise.
var calculateMutationsInDepth = function() {
//Part-1
var depth, promises = [];
for(depth=0; depth<6; depth++) {
promises.push($http.get(urlService(depth))
.then(function(response) {
return response.data;
}, function(error) {
return null; // error recovery - `dataObject` below will be null
}));
}
//Part-2 Aggregate the promises, extract metric data and apply business logic
return $q.all(promises).then(function(data) { // note `return` here
var dataObject, i, j, metricData = [];
for(i=0; i<data.length; i++) {
dataObject = data[i];
if(dataObject != null) {
for(j=0; j<dataObject.length; j++) {
if(dataObject[j].scope === "PRJ" || dataObject[j].scope === "FIL") {
metricData.push(dataObject[j]);
}
}
}
}
// Analyse here, inside the .then()
if(metricData.length > 0) { // metricData is an array and will never be null, therefore test metricData.length.
return analyzeMutationData(metricData);
}
return null;
});
};
calculateMutationsInDepth().then(function(analysis) {
// all complete
// `analysis` is either null or whatever `analyzeMutationData(metricData)` returned.
}).catch(function(error) {
console.log(error);
});
Hope this helps you out! Let me know if it doesn't.

Asynchronously get generated array on function and pass it out of the box [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
Problem resolved on Asynchronously solution to check data from database kinds of loop clause
with this below code i can generate simple array as jsonArray into checkUserMobileNumberAsEwallet function, but i can't pass it out of that to send inside client,
with socket.emit('syncContacts', accountNumbers) i get [] result on accountNumbers, but into if (success) { statement array successful created and pushed into accountNumbers array
socket.on('syncContacts', function (data) {
var accountNumbers = [];
for (var i = 0; i < data.length; i++) {
checkUserMobileNumberAsEwallet(data[i].mobileNumber, function (success) {
if (success) {
accountNumbers.push({ewalletNumber: this.mobileNumber});
console.log(accountNumbers);
}
}.bind({mobileNumber: data[i].mobileNumber}));
}
console.log(accountNumbers);
socket.emit('syncContacts', accountNumbers);
});
function checkUserMobileNumberAsEwallet(mobileNumber, callback) {
var mobileNumber = mobileNumber.substr(1, mobileNumber.length);
var query = "SELECT id FROM userEwallets WHERE ewalletNumber LIKE '%" + mobileNumber + "'";
connection.query(query, function (err, results) {
if (err) return callback(false);
if (results.length === 0)
return callback(false);
else {
return callback(true);
}
});
}
Updated after post comments:
socket.on('syncContacts', function (data) {
//console.log(accountNumbers);
//socket.emit('syncContacts', accountNumbers);
async.parallel(
[
function (callback) {
var accountNumbers = [];
for (var i = 0; i < data.length; i++) {
checkUserMobileNumberAsEwallet(data[i].mobileNumber, function (success) {
if (success) {
accountNumbers.push({ewalletNumber: this.mobileNumber});
console.log(accountNumbers);
}
}.bind({mobileNumber: data[i].mobileNumber}));
}
callback(success, accountNumbers);
}
],
function (success, results) {
console.log("results " + results.toString());
socket.emit('syncContacts', results);
});
});
function checkUserMobileNumberAsEwallet(mobileNumber, callback) {
var mobileNumber = mobileNumber.substr(1, mobileNumber.length);
var query = "SELECT id FROM userEwallets WHERE ewalletNumber LIKE '%" + mobileNumber + "'";
connection.query(query, function (err, results) {
if (err) return callback(false);
if (results.length === 0)
return callback(false);
else {
return callback(true);
}
});
}
Based on our code, it seems like checkUserMobileNumberAsEwallet is an asynchronous event, which accepts a function callback. That would mean that the for-loop would execute, which would queue up executions to checkUserMobileNumberAsEwallet. Immediately after the for-loop executes, console.log would correctly output the empty array accountNumbers and emit the event through the socket. Then the callback functions to each checkUserMobileNumberAsEwallet execution would begin to execute, and log the accountNumbers array which now has data.
This can be solved in a few ways, but likely the easiest and most readable would be to create Promises, and act on the Promises when they resolve. Personally I like the 'when' promise library, but many libraries could help to solve this problem. https://github.com/cujojs/when/blob/master/docs/api.md#whensettle
The problem is that you have no control over when the code inside the callback executes, and it ends up executing after you've already called socket.emit.
You can either a) use an async aggregator to call a callback for each successful mobile number and then, when all of those have finished, call the socket.emit call OR b) do something like what I did below. Alter your checkUserMobileNumberAsEwallet function to accept and verify an array of mobile numbers, and pass the successful numbers to your callback function
Try the following:
socket.on('syncContacts', function (data) {
var accountNumbers = [];
var mobileNumbers = [];
for (var i = 0; i < data.length; i++) {
mobileNumbers.push(data[i].mobileNumber);
}
checkUserMobileNumberAsEwallet(mobileNumbers, function (successfulNumbers) {
if (successfulNumbers.length > 0) {
for (var i = 0; i < successfulNumbers.length; i++) {
accountNumbers.push({ewalletNumber: successfulNumbers[i]});
}
socket.emit('syncContacts', accountNumbers);
}
}.bind({mobileNumbers: mobileNumbers}));
});

Categories

Resources