in my server application i have some method such as:
insert message when user joined to application(insertUserJoinedMessageAndSendMessage)
get unique id of owner mobile numbers (getOwnerPhoneNumberUniqueIds)
print generated data
in this solution i have:
insertUserJoinedMessageAndSendMessage(userPhoneNumbers, assignedUserId.id)
.then(function (t) {
return getOwnerPhoneNumberUniqueIds(userPhoneNumbers);
})
.then(function (result) {
log.info(result);
})
.catch(function (error) {
log.info(error);
});
methods, insertUserJoinedMessageAndSendMessage method work fine without any problem, i want to get generated data on nested Promise as getUserId() from getOwnerPhoneNumberUniqueIds() Promise function, but i can't print data on this part of code as log.info(result); because i can't return array from getOwnerPhoneNumberUniqueIds()
function insertUserJoinedMessageAndSendMessage(userPhoneNumbers, ownerUserId) {
return new Promise(function (resolve, reject) {
let query = "INSERT INTO `userJoinedMobileNumbers` (`id`, `userId`, `nameAndFamily`, `mobileNumber`, `created_at`, `updated_at`) VALUES ";
userPhoneNumbers.map(function (curr) {
query += "(NULL, " + ownerUserId + ", " + curr.contactName + ", " + curr.contactPhone + ", CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),";
});
query = query.substr(0, query.length - 1) + ";";
connection.query(query, function (err, results) {
if (err) return reject(err);
resolve(true);
});
});
}
function getOwnerPhoneNumberUniqueIds(data) {
return Promise.all(data.map(function (curr) {
return getUserId(curr.contactPhone)
.then(function (data) {
log.info("1) " + data);
return {id: data};
});
})).then(function (accountNumbers) {
log.info("2) " + accountNumbers);
return accountNumbers
}).catch(function (err) {
console.log(err);
});
}
function getUserId(contactPhone) {
return new Promise(function (resolve, reject) {
var query = "SELECT id FROM sendMessageUserJoined WHERE `phoneNumber` ='" + contactPhone + "'";
connection.query(query, function (err, results) {
if (err) return reject(err);
if (results.length > 0) {
log.info("0) " + results[0].id);
resolve(results[0].id);
}
});
});
}
in log.info("0) " + results[0].id); i get result of sql command and that return id successful to getOwnerPhoneNumberUniqueIds() and i can print result with this part of code as log.info("1) " + data);, now how can i return that on:
.then(function (accountNumbers) {
log.info("2) " + accountNumbers);
return accountNumbers
})
log.info("2) " + accountNumbers);
dont print result which returned by return {id: data};
The problem is in getUserId.
In some cases you wouldn't resolve or reject the promise. So your Promise.all returned promise maintained pending.
function getUserId(contactPhone) {
return new Promise(function (resolve, reject) {
var query = "SELECT id FROM sendMessageUserJoined WHERE `phoneNumber` ='" + contactPhone + "'";
connection.query(query, function (err, results) {
if (err) return reject(err);
if (results.length > 0) {
log.info("0) " + results[0].id);
resolve(results[0].id);
}
else { //this part is missing
resolve(); //or reject
}
});
});
}
Related
I'm using a for loop for checking the queries I've read before, and after I adding the wrong queries to a map and return the map I received 'undefined'.
I'm using async functions but it didn't help.
This is the function:
async function validateQueryResult(connection, queries) {
var result;
var alertsList = new Map();
return new Promise((resolve, reject) => {
for (query in queries) {
connection.execute({
sqlText: queries[query]["query"],
complete: function (err, stmt, rows) {
if (err) {
console.error('Failed to execute query ' + queries[query]["query"] + ' due to the following error: ' + err.message);
}
else {
rows[0].toString();
result = Object.values(rows[0]);
if (result[0] > 0) {
alertsList.set(queries[query], result[0]);
}
}
}
});
}
resolve(alertsList);
})
}
Thanks for help!
Your problem is, that connection.execute() is another async call. And your for-loop is done before any of the expected answers returns. Try it this way:
async function validateQueryResult(connection, queries) {
var result;
var alertsList = new Map();
return new Promise((resolve, reject) => {
for (query in queries) {
const result = await this.execute(query);
if (result && result[0] > 0) {
alertsList.set(queries[query], result[0]);
}
}
resolve(alertsList);
})
}
async function execute(query) {
connection.execute({
sqlText: queries[query]["query"],
complete: function (err, stmt, rows) {
if (err) {
console.error('Failed to execute query ' + queries[query]["query"] + ' due to the following error: ' + err.message);
}
else {
rows[0].toString();
return Object.values(rows[0]);
}
}
});
}
I'm mapping an array when I finish the mapping I should return the whole array, but in my code the first element that it return.
In this function I'm using async.map to call getShippingMethods function, after getting the result I'm calling resolve function, but the problem the resolve function return just the first element of the array.
function getShippingZones() {
return new Promise((resolve, reject) => {
pool.getConnection(function (error, connection) {
if (error) reject("error in connection")
else {
connection.query("select * from shipping_zone", function (error, result, fields) {
if (error) reject(error)
else {
async.map(result, getShippingMethods, function (res) {
resolve(res)
})
}
})
}
})
})
}
function getShippingMethods(result, callback) {
pool.getConnection(function (error, connection) {
if (error) return (error)
else {
var id = result.id
var name = result.zone_name
console.log("zone name", name)
var query1 = "SELECT sm.id, method_name, rate_amount, rate_min, rate_max, delivry_min, delivry_max, shipping_zone_id, factors_id, factor_name FROM `shipping_method` sm, `factors` f where sm.shipping_zone_id = " + id + " and f.id = sm.factors_id"
var query2 = "SELECT * FROM `country` WHERE shipping_zone_id = " + id
connection.query(query1 + ";" + query2, function (error, results, fields) {
var shippingMethods = results[0].map(_getShippingMethod);
var countries = results[1].map(_getShippedCountries)
callback({ id: id, name: name, shippingMethods: shippingMethods, countries: countries });
});
}
})
}
function _getShippingMethod(result) {
return {
id: result.id,
methodName: result.method_name,
rateAmount: result.rate_amount,
rateMin: result.rate_min,
rateMax: result.rate_max,
deliveryMin: result.delivry_min,
deliveryMax: result.delivry_max,
factorName: {
id: result.factors_id,
name: result.factor_name
}
};
}
function _getShippedCountries(result) {
return {
id: result.id,
name: result.name,
countryCode: result.country_code
};
}
I think you're very nearly at the right result.. the solution should be fairly simple. The async library follows the Node.js pattern of error first callbacks, so when you're returning the result of a successful operation you need to do:
callback(null, result).
or in the case of an error being encountered:
callback(error)
Otherwise async.map thinks we've encountered an error and stops. (See async.map documentation)
In the function getShippingMethods you were calling the callback with the result as the first argument, this should be the second argument - so we don't stop the iteration.
We'll just change the getShippingMethods function like so:
function getShippingMethods(result, callback) {
pool.getConnection(function (error, connection) {
if (error) return (error)
else {
var id = result.id
var name = result.zone_name
console.log("zone name", name)
var query1 = "SELECT sm.id, method_name, rate_amount, rate_min, rate_max, delivry_min, delivry_max, shipping_zone_id, factors_id, factor_name FROM `shipping_method` sm, `factors` f where sm.shipping_zone_id = " + id + " and f.id = sm.factors_id"
var query2 = "SELECT * FROM `country` WHERE shipping_zone_id = " + id
connection.query(query1 + ";" + query2, function (error, results, fields) {
if (error) {
// Our query failed, so we pass error as the first argument to the callback.
callback(error)
} else {
var shippingMethods = results[0].map(_getShippingMethod);
var countries = results[1].map(_getShippedCountries)
// This is an error first callback, so pass null here since our query was successful
callback(null, { id: id, name: name, shippingMethods: shippingMethods, countries: countries });
}
});
}
})
}
And we need to change getShippingZones like so:
function getShippingZones() {
return new Promise((resolve, reject) => {
pool.getConnection(function (error, connection) {
if (error) reject("error in connection")
else {
connection.query("select * from shipping_zone", function (error, result, fields) {
if (error) reject(error)
else {
// Change to an error first callback.
async.map(result, getShippingMethods, function (err, res) {
if (err) {
reject(err);
} else {
resolve(res);
}
})
}
})
}
})
})
}
I am having trouble setting up my promise in my code. This is what I have implemented right now:
let promise = new Promise(function(resolve, reject){
ATVStatement();
let isdone = true;
if(isdone){
resolve();
}else{
reject();
}
})
promise.then(CustomMessage());
The problem I am getting is that the I pretty sure that the promise is not working. If anyone could spot the error that I am doing I would greatly appreciate it.
EDIT
Here is code for the the two functions being used:
function ATVStatement() {
request = new Request("select distinct(\"Product Name\") from SPA_Data_Feeds where \"Strategic Priority\" = 'Accelerate to Value (LD)'",
function(err, rowCount, rows)
{
console.log(rowCount + ' row(s) returned');
}
);
//var result = "";
var count = 0
request.on('row', function(columns) {
columns.forEach(function(column) {
console.log("%s\t", column.value);
result+= column.value + "\t\n"; //result is a global variable
count++;
});
});
connection.execSql(request);
}
function CustomMessage(){
console.log('here')
var customMessage = new builder.Message(session)
.text("### Here is a list of the ATV Compounds: \n" + "> %s ", result)
.textFormat("markdown")
.speak("Here is what I found for \'%s\'.", session.message.text)
.textLocale("en-us");
session.send(customMessage); }
Ok, so now we understand the code a little better (thanks for updating), we need to modify the ATVStatement to return a promise and resolve it when the query is complete.
We change the code above to:
let atvPromise = ATVStatement();
atvPromise.then ((result) => {
CustomMessage();
}).catch ( (err) => {
console.log('An error occurred: ' + err);
});
function ATVStatement() {
return new Promise(function(resolve, reject) {
request = new Request("select distinct(\"Product Name\") from SPA_Data_Feeds where \"Strategic Priority\" = 'Accelerate to Value (LD)'",
function(err, rowCount, rows)
{
console.log(rowCount + ' row(s) returned');
if (err) {
reject(err);
} else {
resolve(rows);
}
}
);
var count = 0
request.on('row', function(columns) {
columns.forEach(function(column) {
console.log("%s\t", column.value);
result+= column.value + "\t\n"; //result is a global variable
count++;
});
});
connection.execSql(request);
});
}
You see, ATVStatement was actually asynchronous.
I'm concatenating tweets from a defined user through a helper file and trying to retrieve it in my server.js but there the str value is still undefined (and this line gets executed first), then the console.log from my helper prints with the right value.
Output:
GET /login/twitter/callback 302 618.242 ms - 0
Concatenated Tweets in Server: undefined
Concatenated Tweets in Helper: Test Tweet 3 #TestTweet Test Tweet 2
Test Tweet 1
Can anyone help on what control flow I should use to call twitterHelper.getTweets functions to get the returned str in the server please? Thanks!
Server.js
app.get('/login/twitter/callback',
passport.authenticate('twitter', {failureRedirect: "/login"},
function(req, res) {
// auth success
async.waterfall ([
function(callback) {
callback(null, twitterHelper.getTweets(user));
},
function(str, callback) {
console.log("Concatenated Tweets in Server: " + str);
callback(null);
}
],
function(err) {
if(err)
console.log("Error: " + err);
}
);
}
)
);
Helper.js
var concatTweets = '';
var promise = new Promise(
function(resolve, reject) {
T.get('statuses/user_timeline', params, function( err, data, response) {
if(err)
reject(err);
else {
for (var i = 0; i < data.length ; i++)
concatTweets = concatTweets + " " + data[i].text;
resolve(concatTweets);
}
})
}
).then(
str => {
console.log("Concatenated Tweets in Helper: " + str);
return str;
}, err => {
console.log(err);
return err;
}
);
Instead of using this longway you can use this simple way by promise.
Helper.js
var concatTweets = '';
var getTweets = function(user){
var promise = new Promise(function(resolve, reject) {
T.get('statuses/user_timeline', params, function( err, data, response) {
if(err){
reject(err);
} else {
for (var i = 0; i < data.length ; i++)
concatTweets = concatTweets + " " + data[i].text;
console.log("Concatenated Tweets in Helper: " + concatTweets);
resolve(concatTweets);
}
})
});
return promise;
}
Server.js
app.get('/login/twitter/callback', passport.authenticate('twitter', {failureRedirect: "/login"},function(req, res) {
// auth success
twitterHelper.getTweets(user).then(str=>{
console.log("Concatenated Tweets in Server: " + str);
}).catch(err=>{
console.log("Error: " + err);
});
}));
I hope this will work for you.
I wrote the following module which connects to postgresql or SQLServer depending a the Type var value:
exports.GetQueryResult = function (type, Name,con,callback) {
var sql='';
if (Type ='PG') {
sql=sql + ' SELECT …………..';
pg.connect(con, function(err, client, done) {
if(err) {
console.log("Error :" + err);
return callback(err);
}
client.query(sql,[Name], function(err, Result) {
if(err) {
console.log("Error: " +err);
return callback(err);
}
return callback(null,Result);
done();
});
});
}
else
{
sql=sql + ' SELECT …..';
sql.open(con, function (err, conn,done) {
if (err) {
console.log("Error :" + err);
return callback(err);
}
conn.queryRaw(sql,Name, function (err, Result) {
if (err) {
console.log("Error ejecutando la consulta. Error: " +err);
return callback(err);
}
callback(null,Result);
done;
});
});
}
};
I call this function from:
var MultiBD = require('./MultiBD.js');
var LayerType=['PG','SQL','PG'];
var con=’’;
for (var i=1; i<=Layers.length; i++) {
if (Layers[i-1]!=undefined){
con=MultiBD.conexion(LayerType [i-1],server,BD);
MultiBD.GetQueryResult(LayerType[i-1], Name[i-1],con,
function (err,Result){
console.log('Result : ' + Result.rows.length);
}
);
}
}
The results are:
Result : 111
Result : 2888
Result : 5
I get three query results. The first one returns 111 rows, the second one 2888 and the third one 5.
What i need is to get only one unique result with all the 3004 rows (111+2888+5).
Regards,
You'll need some flow control to wait until all 3 methods have completed their DB calls before printing their results. There are packages for this, either of the callback variety or Promises.
Here is an example using the async package (which is of the callback variety of flow-control):
var async = require('async');
var totalResults = 0;
async.each( Layers, function eachMethod( layer, eachCb ){
var con = MultiBD.conexion(layer,server,BD);
MultiBD.GetQueryResult(LayerType[i-1], Name[i-1],con, function (err,Result) {
if( err ) return eachCb( err );
totalResults += Result.rows.length;
eachCb( null );
} );
}, function finalEach( eachErr ){
if( eachErr ) console.log( "There was an error. " + eachErr );
else console.log( "Result: " + totalResults );
});
In this call, a method (the eachMethod) is called on every item in the array. The results are stored in a shared variable. When all items have completed, the finalEach method is called. Here errors have bubbled up (which you can choose to check or not) and all calls have completed, so we can simply print the value of the shared variable.