I have a code to do some calculation.
How can I write this code in an asyn way?
When query the database, seems we can not get the results synchronously.
So how to implement this kind of feature?
function main () {
var v = 0, k;
for (k in obj)
v += calc(obj[k].formula)
return v;
}
function calc (formula) {
var result = 0;
if (formula.type === 'SQL') {
var someSql = "select value from x = y"; // this SQL related to the formula;
client.query(someSql, function (err, rows) {
console.log(rows[0].value);
// *How can I get the value here?*
});
result = ? // *How can I return this value to the main function?*
}
else
result = formulaCalc(formula); // some other asyn code
return result;
}
Its not possible to return the result of an asynchronous function, it will just return in its own function scope.
Also this is not possible, the result will always be unchanged (null)
client.query(someSql, function (err, rows) {
result = rows[0].value;
});
return result;
Put a callback in the calc() function as second parameter and call that function in the client.query callback with the result
function main() {
calc(formula,function(rows) {
console.log(rows) // this is the result
});
}
function calc(formula,callback) {
client.query(query,function(err,rows) {
callback(rows);
});
}
Now if you want the main to return that result, you also have to put a callback parameter in the main and call that function like before.
I advice you to check out async its a great library to not have to deal with this kind of hassle
Here is a very crude way of implementing a loop to perform a calculation (emulating an asynchronous database call) by using events.
As Brmm alluded, once you go async you have to go async all the way. The code below is just a sample for you to get an idea of what the process in theory should look like. There are several libraries that make handling the sync process for asynch calls much cleaner that you would want to look into as well:
var events = require('events');
var eventEmitter = new events.EventEmitter();
var total = 0;
var count = 0;
var keys = [];
// Loop through the items
calculatePrice = function(keys) {
for (var i = 0; i < keys.length; i++) {
key = keys[i];
eventEmitter.emit('getPriceFromDb', {key: key, count: keys.length});
};
}
// Get the price for a single item (from a DB or whatever)
getPriceFromDb = function(data) {
console.log('fetching price for item: ' + data.key);
// mimic an async db call
setTimeout( function() {
price = data.key * 10;
eventEmitter.emit('aggregatePrice', {key: data.key, price: price, count: data.count});
}, 500);
}
// Agregate the price and figures out if we are done
aggregatePrice = function(data) {
count++;
total += data.price;
console.log('price $' + price + ' total so far $' + total);
var areWeDone = (count == data.count);
if (areWeDone) {
eventEmitter.emit('done', {key: data.key, total: total});
}
}
// We are done.
displayTotal = function(data) {
console.log('total $ ' + data.total);
}
// Wire up the events
eventEmitter.on('getPriceFromDb', getPriceFromDb);
eventEmitter.on('aggregatePrice', aggregatePrice);
eventEmitter.on('done', displayTotal);
// Kick of the calculate process over an array of keys
keys = [1, 2, 3]
calculatePrice(keys);
Related
Sorry, I read about handling async functions in order to get variable names from them but I am not sure what I am doing wrong and how to handle it.
for(let j = 0; j < ga.length; j++) {
var sql = "SELECT * FROM matches WHERE clh = '"+ga[j]+"'"
const dbq = db.query(sql, function(err, result) {
if (err) console.log(err);
var gs1 = 0;
var gs2 = 0;
var pts1 = 0;
var w1 = 0;
var d1 = 0;
var l1 = 0;
for (let i = 0; i < result.length; i++) {
gs1 += result[i].gsh;
gs2 += result[i].gsa;
const r = mgs1(result[i].gsh, result[i].gsa);
if (r == 3) w1 += 1;
if (r == 1) d1 += 1;
if (r == 0) l1 += 1;
var gd1 = gs1 - gs2;
var r1 = [result[i].clh, result.length, w1, d1, l1, gs1, gs2, gd1 ];
}
gs4.push(r1);
if (gs4.length == 6) {
return gs4;
}
})
}
}
This function returns the array that I want but I am not sure how to access it outside the db.query block. I read posts about handling variables from async functions but I just can't seem to do it in this example. Thanks a lot in advance
I guess you have defined const gs4 = [] somewhere in code you did not show us. That's part of the answer to your question: it will be populated after your callback from db.query() completes.
The rest of the answer: it is not populated until after the callback completes. Also, the return from inside your callback is meaningless; it just returns to db.query() .
Also, db.query() returns to its caller instantly, long before it calls its callback. So your loop tries to run multiple queries concurrently. I guess the result in gs4 will accumulate the results from all the queries.
With respect, I believe a quick jump up the learning curve for Promises or async / await lies in your near future.
This may help : node.js mysql query in a for loop
If you would like to query the database the correct way, you should use the embedded functions that comes with database driver. For string interpolation and returning data for your functions.
exports.lookupLogin = (req, res, next) => {
let sql = 'SELECT e.employee_id, e.login, e.password FROM employee e WHERE e.login=?';
postgres.client.query(sql, [req.body.login], (error, result, fields) => {
if (err) {
return res.status(500).json({ errors: ['Could not do login'] });
}
res.status(200).json(result.rows);
});
};
For more information you can check the mysql documentation to use with nodejs.
I am working with NodeJS using ExpressJS framework in a mysql backend. I am running a query inside a for loop and my loop and work afterwards depends on the return value of the query. I am not very good with mysql query so I am running it through a for loop.
The problem is, due asynchronous [I guess!], the for loop ends long before the query result comes out.
Here is my code:
function search_people_step2(user_id, search_criteria, user_friend)
{
var first_name_friends = [];
var last_name_friends = [];
for(var i = 0; i < user_friend.length; i++)
{
con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
{
if(err)
{
//error;
}
else
{
if(rows.length == 0)
{
//nothing gets returned
}
else {
console.log(rows);
first_name_friends[i] = rows[0].first_name;
last_name_friends[i] = rows[0].second_name;
}
}
});
}
Now,I can get the value (using console.log) inside the query statement, however, on the outside, the value becomes empty (undefined) since the rest of the code has already been computed.
How can I solve this?
Thanks in advance.
The first thing that I find weird in your code is that you are not using an IN statement in your SQL query (not directly related to your problem though) which means you are making as many requests as there are entries in user_friend. The problem is that the SQL library is implemented asynchronously and you cannot avoid it. BUT you can handle it elegantly with Promises which are ES6 features:
(I didn't test the code but I think it should work)
function search_people_step2(user_id, search_criteria, user_friend)
{
return new Promise((resolve,reject)=>{
var first_name_friends = [];
var last_name_friends = [];
var placeHolders=user_friend.map(()=>"?").join(",");
con.query("SELECT first_name, second_name FROM user WHERE userid IN ("+placeHolders+")",user_friend,(err,rows)=>{
if(err)
reject(err);
else{
rows.forEach(row=>{
first_name_friends.push(row.first_name);
last_name_friends.push(row.second_name);
});
resolve({first_name_friends,last_name_friends});
}
});
});
}
And call your function like this :
search_people_step2(id,crit,friends).then(result=>{
//handle result asynchronously as there is no choice
console.log(result.first_name_friends);
console.log(result.last_name_friends);
}).catch(err=>{
//handle error
});
You are right, your problem is the asynchronous nature of the mysql call. You have to provide a callback to your search_people_step2 function.
You may change it like this:
search_people_step2(user_id, search_criteria, user_friend, callback)
In your function body you may use a library called async to handle all the callbacks properly. Here is an example for the usage:
async.eachSeries(user_friend, function(item, eachCb){
con.query("SELECT first_name, second_name FROM user WHERE userid = ?",
user_friend[i],function(err, rows) {
if(err) {
eachCb('error');
}
else {
if(rows.length == 0){
//nothing gets returned
eachCb(null);
}
else {
console.log(rows);
first_name_friends.push(rows[0].first_name);
last_name_friends.push(rows[0].second_name);
eachCb(null);
}
}
}, callback);
});
This calls each query in order on every item of the array and calls the inner callback if finished. When all items are processed or an error occured the outer callback is called. See the async library for further documentation.
simplest solution is
function search_people_step2(user_id, search_criteria, user_friend)
{
var first_name_friends = [];
var last_name_friends = [];
for(var i = 0; i < user_friend.length; i++)
{
con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
{
if(err)
{
//error;
}
else
{
if(rows.length == 0)
{
//nothing gets returned
}
else {
console.log(rows);
first_name_friends[i] = rows[0].first_name;
last_name_friends[i] = rows[0].second_name;
}
if(i==user_friend.length-1){
//do your work here which you want to perform in end
}
}
});
}
or use async library
var async = require('async');
var first_name_friends = [];
var last_name_friends = [];
async.series([function(cb){
function search_people_step2(user_id, search_criteria, user_friend)
{
for(var i = 0; i < user_friend.length; i++)
{
con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
{
if(err)
{
//error;
}
else
{
if(rows.length == 0)
{
//nothing gets returned
}
else {
console.log(rows);
first_name_friends[i] = rows[0].first_name;
last_name_friends[i] = rows[0].second_name;
}
if(i==user_friend.length-1){
cb()
}
}
});
}
},function(cb){
//do your work here
}],function(err){})
I have a big json file which is changed quite often (translation values). I need to check whether properties in this json are sorted alphabetically. Perfectly I'd like to use gulp for that and run this check in CI environment so when properties aren't ordered an error is reported.
I've searched for it but haven't found anything. Are you aware of any existing solution dealing with this problem?
You could do it purely with node.js and hook it into your gulp build process easily enough:
var fs = require('fs');
fs.readFile("data.json", { encoding: 'utf8' }, function(err, data) {
if (err) throw err;
var dataObj = JSON.parse(data).mystuff;
//check if it's ordered
for (var i = 1; i < dataObj.length; i++) {
if (dataObj[i].toLowerCase() < dataObj[i-1].toLowerCase()) { //note that depending on what it is you're ordering, this might need to change...
throw new Error('THIS IS OUT OF ORDER! I TOLD YOU! I TOLD YOU!!!! KEEP IT ORDERED! AND WHAT DID YOU DO?!!! UNACCEPTABLE!!!');
}
}
console.log('Ordered correctly! Good work!');
});
And the data.json that I used to test it, ordered incorrectly:
{
"mystuff": [
"Super",
"Duper",
"Happy",
"Fun"
]
}
And ordered correctly:
{
"mystuff": [
"Duper",
"Fun",
"Happy",
"Super"
]
}
And if you want to take it a step further and have the process automatically re-order the file for you, you could do something like this:
var fs = require('fs');
var fileName = 'data.json';
var fileOptions = { encoding: 'utf8' };
fs.readFile(fileName, fileOptions, function(err, data) {
if (err) throw err;
debugger;
var dataObj = JSON.parse(data);
var dataObjOrdered = orderObj(dataObj);
var jsonOrdered = JSON.stringify(dataObjOrdered, null, 4);
fs.writeFile(fileName, jsonOrdered, fileOptions, function(err) {
if (err) throw err;
console.log('JSON data fields are ordered huzzah!');
});
});
/**
* Returns an object with the same data, but with its fields ordered.
* Note that we leave array values in the same order they're already in
*/
function orderObj(obj) {
if (!isObject(obj)) {
return obj;
}
//arrays are left alone. if you want to order them too, do it inside this block
if (Array.isArray(obj)) {
return obj;
}
//load up an array containing all our object's fields
var fields = [];
for (var field in obj) {
fields.push(field);
}
//sort the fields alphabetically (remember to do this compare case-insensitively since utf-8 capital letters come before lowercase letters)
fields.sort(function (a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a > b) {
return 1;
}
if (a < b) {
return -1;
}
return 0;
});
//return the new object with ordered fields, recursively ordering sub objects' fields
var result = {};
for(var i = 0; i < fields.length; i++) {
var field = fields[i];
var value = obj[field];
result[field] = orderObj(value);
}
return result;
}
function isObject(value) {
// http://jsperf.com/isobject4
return value !== null && typeof value === 'object';
}
Thanks in advance for any help. I have spent a couple of weeks scouring the web for some insight. I have been developing code for over 50 years but I am a fairly new JavaScript, HTML, knockout. From what I see, this will be great if I can figure out how to make it work. The example given below is only one of the many things I have tried. Please advise.
I have defined two variables as observables, one computed observable, and one observableArray in my view model. Within the document.ready function, I make an Ajax call which returns a json in object notation. ( I checked it in the debugger). When my HTML page displays the observables and computed observables show up properly. The
observable array generates an error (see below) and then displays data obtained from only the first row returned from Ajax. (two were returned).
How do I adjust my code so that all the rows in the Ajax data are shown in the displayed HTML?
Here is the error message that I get:
Uncaught ReferenceError: Unable to process binding "foreach: function (){return cartItems }"
Message: Unable to process binding "text: function (){return itemSubTotal() }"
Message: itemSubTotal is not defined (19:41:39:172 | error, javascript)
Here is my global data for the view model:
var cartDB = "";
var totalPrice = "100";
var cartItems = new Array;
Here is the view model:
var ViewModel =function (){
// direct items ==
this.cartDB = ko.observable(cartDB);
// array itesm
// this.cartItems = ko.observableArray(cartItems);
this.cartItems = ko.mapping.fromJS(cartItems);
//for (var n = 1;n++;n<cartItems.length){ this.cartItems.push(cartItem[n]);}
// computed items
this.totalPriceSv = ko.computed(function(){
return "Total Price*=" + centsToDollars(totalPrice);// todo fix
} ,this);//end totalSvPrice
};// end ViewModel
The data is obtained from the following routine which calls on Ajax.This routine is called once from within document.ready and obtains the expected data on the success callback.
function databaseCart(commandInput, cartDBInput, cartPidInput,logPhpInput) {
var postData = new Array();
postData[0] = commandInput;
postData[1] = cartDBInput;
postData[2] = cartPidInput;
postData[3] = logPhpInput; //debug log on php side
data = null; //for return values
$.ajax({
type: "GET",
url: 'ww_CartProcess.php', //the script to call to get data
data: {data: postData},
dataType: 'json',
success: function(data) {
cartItems = data;
debug = 0;
},
complete: function(data) {
ko.applyBindings(new ViewModel);
return TRUE;
},
error: function(x, e) {//this is the error function for ajax
var xErr = x;
var eErr = e;
ajaxErrorProcessing(xErr, eErr, "addToCart", postData);
}
});
}// end databasecart
Here is the HTML 5 snippet.
<div>
<h1 id="cartTitle">Cart Number: <span data-bind="text: cartDB"> </h1>
<div class ="boxCartItem" data-bind="foreach:cartItems" >
<div class ="boxItemTitle">
<h2 data-bind="text: title()"></h2>
</div><!--boxItemTitle-->
<div class ="cartItemBottom"></div>
</div ><!--class ="boxCartItem"-->
My thanks to the commenters. I still do not know how to add an element to all item rows in an observable array, but this problem was caused by not having the item listed defined. Clue> When multiple errors are presented it is sometimes ( and maybe always) good to work from the bottom up.
The problem can be better stated as : Given a 2 x 17 array (2 rows and 17 columns of independent vars)create an observableArray that contains 2 rows and 17 plus columns consisting of the 17 independent variables (can only be changed in the database or by limited user input) augmented with a large number of computed functions .
1.0 I created an orderModel which contained the ko.computed(functions() for each dependent variable.
function rowOrder(data) {
var self = this;
var model = ko.mapping.fromJS(data, {}, self);
model.imagePathSv = ko.computed(function() {
var category = self.category();
var imageName = self.imageName();
var sv ="";
var sv = "products\\" +category+"\\" + imageName;
return sv;
});//end model.imagePathSv
//original offer
model.origNumValidate = ko.computed(function() {
ans = self.origNum();
if (ans < 0 | ans > 1) {
alert("\
Only onw Original can be pruchased");
ans = 1;
}
self.origNum(ans);
return ans;
});//originalNumValidate
model.origNumLabel = ko.computed(function() {
var sv = "Original..." + centsToDollars(self.origCost());
return sv;
});//end model.origNumLabel
model.origMattedShow = ko.computed(function() {
if (self.origMattedCost() > 0) {
return true;
}
else {
return false;
}
});
model.origMattedLabel = ko.computed(function() {
var sv = "Matting...." + centsToDollars(self.origMattedCost());
return sv;
});
model.origFramedShow = ko.computed(function() {
if (self.origFramedCost() > 0) {
return true;
}
else {
return false;
}
});
model.origFramedLabel = ko.computed(function() {
var sv = "Framing...." + centsToDollars(self.origFramedCost());
return sv;
});
//reproductons offer
model.reproNumValidate = ko.computed(function() {
ans = self.reproNum();
self.reproNum(ans);
return ans;
});
model.reproNumLabel = ko.computed(function() {
var sv = "Reproductions." + centsToDollars(self.reproCost()) + " each";
return sv;
});//end model.reproNumLabel
model.reproMattedShow = ko.computed(function() {
if (self.reproMattedCost() > 0) {
return true;
}
else {
return false;
}
});
model.reproMatted = ko.observable(true);
model.reproMattedLabel = ko.computed(function() {
var sv ="Matting...." +centsToDollars(self.reproMattedCost());
return sv;
});
model.reproFramedShow = ko.computed(function(){
if(self.reproFramedCost()>0){return true;}
else {return false;}
});
model.reproFramed = ko.observable(true);
model.reproFramedLabel = ko.computed(function() {
var sv ="Framing...." +centsToDollars(self.reproFramedCost());
return sv;
});
//pricing subTotals
model.productsSubTotal = ko.computed(function() {
var ans =self.origNum() * self.origCost() + self.reproNum() * self.reproCost();
return ans;
});//end model.productsSubTotal
model.productsSubTotalSv = ko.computed(function() {
return "Products.." +centsToDollars(model.productsSubTotal());
return ans;
});//end model.productsSubTotal
model.mattingSubTotal = ko.computed(function() {
return self.origNum() * self.origMattedCost() + self.reproNum() * self.reproMattedCost();
});//end model.mattingSubTotal
model.mattingSubTotalSv = ko.computed(function() {
return "Matting...." +centsToDollars(model.mattingSubTotal());
});//end model.mattingSubTotal
model.framingSubTotal = ko.computed(function() {
return self.origNum() * self.origFramedCost() + self.reproNum() * self.reproFramedCost();
});//end model.framingSubTotal
model.framingSubTotalSv = ko.computed(function() {
return "Framing...." +centsToDollars(model.framingSubTotal());
});//end model.productsSubTotal
model.rowSubTotal = ko.computed(function() {
return model.productsSubTotal() +model.mattingSubTotal() + model.framingSubTotal();
});//end model.rowsSubTotal
model.rowSubTotalSv = ko.computed(function() {
return "Item Subtotal.." +centsToDollars(model.rowSubTotal());
});//end model.productsSubTotal
};// END rowOrder
2.0 I created a mapping variable as follows:
var mapping = {
create: function(options) {
return new rowOrder(options.data);
}
};
3,0 I created a View Model as follows:
function ViewModel() {
// direct items for whole page
var self = this;
this.cartId = ko.observable(cartDB);
this.cartIdSv = ko.computed(function() {
var sv = "Cart Number: "+ self.cartId();
return sv;
},this);//
this.totalPrice = ko.computed(function() {//to DO COMPUTE
var int = 10000;
return int;
},this);
this.totalPriceSv = ko.computed(function(){
return "Total Price*: " + centsToDollars(this.totalPrice());
},this);
// by row items
this.cartItems = ko.mapping.fromJS(cartItems, mapping);
}// end ViewModel
4.0 In the success call back from ajax :
success: function(data) {
cartItems = data;
ViewModel();
5.0 I put the ko.apply.bindings(new ViewModel) in the ajax complete callback.
The result was that my rather involved page came up as expected with the computed values initially set.
I am still working on how to update this page. I have not been able to get my code to recompute the computed variables when the user clicks or unclicks a checkbox.
I could not have done this without suggestions from the stackOverflow group. Thanks to you all for the posts that I found all over the google.
I have created a database called todo with table name todo having fields like title,date etc. The table is empty right now.
I have defined a function which takes title as parameter and check whether table contains that title or not.
It should return the number of rows.
GetTitle function:
todo.webdb.GetTitle = function(title) {
var db = todo.webdb.db;
db.transaction(function(tx){
tx.executeSql("SELECT title FROM todo WHERE title=?", [title],
loadTitle,
todo.webdb.onError);
});
}
loadTitle Function:
function loadTitle(tx, rs) {
return rs.rows.length;
}
GetTitle Function is called:
row=todo.webdb.GetTitle("Hello");
alert(row);
I get alert 'undefined' it should return 0
I am confused right now how to resolve this issue.
I think the transactions are asynchronous, so you need to callback function to receive the value.
todo.webdb.GetTitle = function(title, callback) {
var db = todo.webdb.db;
db.transaction(function(tx){
tx.executeSql("SELECT title FROM todo WHERE title=?", [title],
(function loadTitle(tx, rs){
callback( rs.rows.length );
}),
todo.webdb.onError);
});
}
todo.webdb.GetTitle( "Hello", function(count){
alert( "count = " + count );
});
Here's an excellent tutorial for more information.
http://blog.darkcrimson.com/2010/05/local-databases/
Update
Don't make functions inside a loop for this main reason.
Both i and title are referencing a value created out of local scope.
So the last value of assigned to both i and title will be displayed.
for (i = 0; i < itemcount; i++) {
alert('i outside if:' + i);
var title = x[i].getElementsByTagName("title")[0].childNodes[0].nodeValue;
todo.webdb.GetTitle( title, function (count) {
if (!count) {
alert('i inside if:' + i);
alert( title );
}
});
}
Fix:
Generate a function that has the values locked in a closure.
var createTitleCallBack = function( i, title ){
return function (count) {
if (!count) {
alert('i inside if:' + i);
alert( title );
}
};
};
for (i = 0; i < itemcount; i++) {
alert('i outside if:' + i);
var title = x[i].getElementsByTagName("title")[0].childNodes[0].nodeValue;
todo.webdb.GetTitle( title, createTitleCallBack( i, title ) );
}