I am having db.js with db related functions, I want to make call to db.js and wait until it returns the query result.
But the result is returned after the execution of the db call. Can anyone please help how to solve this.
Code sample:
var Q = require('q');
db= require("./dbaccess.js");
function waitfor(ms){
var deferred = Q.defer();
setTimeout(function() {
deferred.resolve(db);
}, 5000);
return deferred.promise;
}
waitfor(2000).done(function(dbcall) {
console.log('contrived example '+ dbcall.query1());
});
dbacess.js:
var sql = require('mssql');
var config = {
user: 'xx',
password: 'xxx',
server: 'aaa',
database: 'RequestCenter',
stream: true,
}
this.query1=function() {
sql.connect(config, function(err) {
var result;
var request = new sql.Request();
request.query("select * from dbo.AcAccount where Name like 'AutomationCli%' ");
request.on('row', function(row) {
console.log(row.Name);
result = row.Name;
});
request.on('error', function(err) {
console.log("err : "+err);
});
request.on('done', function(returnValue) {
console.log("done");
});
return result;
});
sql.on('error', function(err) {
console.log("sql err : "+err);
});
}
Output:
contrived example undefined
in db: AutomationClient
Expected output:
in db: AutomationClient
contrived example AutomationClient
Not sure why your main code passes in 2000 for the ms argument and then does a 5000ms timeout, in fact, why are you doing a timeout at all, if that was some attempt to wait for the db function to complete, then you don't need it
If you must use promises - personally I'd use a simple callback for such simple code, however, I get that you want to learn how to use Promises
Your original code looked like it was attempting to return the value of the LAST row.name
This code returns an array of row.name
Not knowing the type of data you'd be getting, I don't know which is correct
dbacess.js
var Q = require('q');
var sql = require('mssql');
var config = {
user: 'xx',
password: 'xxx',
server: 'aaa',
database: 'RequestCenter',
stream: true,
}
this.query1 = function() {
var deferred = Q.defer();
sql.connect(config, function(err) {
var result = []; // return all rows - modify as required
var request = new sql.Request();
request.query("select * from dbo.AcAccount where Name like 'AutomationCli%' ");
request.on('row', function(row) {
console.log(row.Name);
result.push(row.Name);
});
request.on('error', function(err) {
console.log("err : " + err);
deferred.reject(err);
});
request.on('done', function(returnValue) {
deferred.resolve(result);
});
});
sql.on('error', function(err) {
console.log("sql err : " + err);
deferred.reject(err);
});
return deferred.promise;
}
Code sample:
db = require("./dbaccess.js");
db.query1().then(function(result) {
console.log('contrived example ' + result);
});
Related
I'm trying to structure this code so I can call getStudent from more than one place. I'm experimenting with writing some JSON routines. I was trying to the err first callback pattern. It's not the error so much that disturbs me, but that the error is being caught in the one of the catches inside getStudent.
Incidentally, I figured out the error is to do status(200) instead of status(0).
How should I restructure so those catches so they don't impact the main code? Or am I totally misusing the callback concept? Seems like the "then/catch" is the proper way to handle async with mssql.
var express = require('express');
var app = express();
// config for your database
var config = {
user: 'ReadOnlyUser1',
password: 'whatever',
server: 'localhost\\SQLEXPRESS',
database: 'StudentsOld'
};
var lookupStudentId = 31;
const sql = require('mssql');
var connPool = new sql.ConnectionPool(config);
function getStudent(studentId, callback) {
console.log("Starting getStudent");
nullResult = {};
connPool.connect().
then (function() {
console.log('Connected - starting query');
var request = new sql.Request(connPool);
var sqlQuery = 'select student_firstname, student_lastname from students where student_id = ' + studentId;
request.query(sqlQuery).
then(function (recordSet) {
console.log('Query completed');
connPool.close();
console.log("recordSet=");
console.dir(recordSet);
callback(nullResult, recordSet);
}).catch(function (queryErr) {
console.log('Error in database query: ' + queryErr);
callback('Error in db query: ' + queryErr, nullResult);
});
}).catch(function (connErr) {
console.log('Error in database connection: ' + connErr);
callback('Error in db conn: ' + connErr, nullResult);
});
console.log('fall thru 1');
}
function isEmptyObject(obj) {
return !Object.keys(obj).length;
}
app.get('/student', function(request, response){
console.log('Neal Test1');
getStudent(lookupStudentId, function(err, result){
console.log('Back from getStudent');
if(!isEmptyObject(err)) {
console.log("error400=" + err);
console.log("empty test=" + Object.keys(err).length);
response.status(400).send(err);
}
else
{
console.log("result=");
console.dir(result);
console.log('about to send back status=0');
response.status(0).send(result);
}
})
return;
});
app.listen(3000, function () {
console.log('Express server is listening on port 3000');
});
I run the above by entering: http://localhost:3000/student in the browser.
The Console Output is:
C:\Software\nodejs\myapp>node index.js
Express server is listening on port 3000
Neal Test1
Starting getStudent
fall thru 1
Connected - starting query
Query completed
recordSet=
{ recordsets: [ [ [Object] ] ],
recordset:
[ { student_firstname: 'Jonah ',
student_lastname: 'Hill ' } ],
output: {},
rowsAffected: [ 1 ] }
Back from getStudent
result=
{ recordsets: [ [ [Object] ] ],
recordset:
[ { student_firstname: 'Jonah ',
student_lastname: 'Hill ' } ],
output: {},
rowsAffected: [ 1 ] }
about to send back status=0
Error in database query: RangeError: Invalid status code: 0
Back from getStudent
error400=Error in db query: RangeError: Invalid status code: 0
empty test=53
Revision 1:
function getStudent(studentId) {
console.log("Starting getStudent");
recordset = {};
connPool.connect().
then (function() {
console.log('Connected - starting query');
var request = new sql.Request(connPool);
var sqlQuery = 'select student_firstname, student_lastname from students where student_id = ' + studentId;
request.query(sqlQuery).
then(function (recordSet) {
console.log('Query completed');
connPool.close();
console.log("recordSet=");
console.dir(recordSet);
return recordset;
}).catch(function (queryErr) {
console.log('Error in database query: ' + queryErr);
return queryErr;
});
}).catch(function (connErr) {
console.log('Error in database connection: ' + connErr);
return connErr;
});
console.log('fall thru 1');
}
app.get('/student', function(request, response){
console.log('Neal Test1 - start app.get for /student');
getStudent(lookupStudentId)
.then (function(recordset) {
console.log('Back from getStudent, recordSet=');
console.dir(recordSet);
response.status(200).send(recordset);
})
.catch (function(err) {
console.log("error400=" + err);
console.log("empty test=" + Object.keys(err).length);
response.status(400).send(err);
})
return;
});
Results of Revision 1:
Express server is listening on port 3000
Neal Test1 - start app.get for /student
Starting getStudent
fall thru 1
TypeError: Cannot read property 'then' of undefined
at C:\Software\nodejs\wisdomcalls\index.js:55:9
at Layer.handle [as handle_request] (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\layer.js:95:5)
at C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\index.js:335:12)
at next (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\index.js:275:10)
at expressInit (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\middleware\init.js:40:5)
at Layer.handle [as handle_request] (C:\Software\nodejs\wisdomcalls\node_modules\express\lib\router\layer.js:95:5)
Connected - starting query
Query completed
recordSet=
{ recordsets: [ [ [Object] ] ],
recordset:
[ { student_firstname: 'Jonah ',
student_lastname: 'Hill ' } ],
output: {},
rowsAffected: [ 1 ] }
From the log, you can see that the main function is running before the database is even connected.
Revision 2:
This seems to return maybe the connection instead of the query? See console.log "undefined".
function getStudent(studentId) {
console.log("Starting getStudent");
recordset = {};
return connPool.connect()
.then (function() {
console.log('Connected - starting query');
var request = new sql.Request(connPool);
var sqlQuery = 'select student_firstname, student_lastname from students where student_id = ' + studentId;
return request.query(sqlQuery)
;
/*
.then(function (recordSet) {
console.log('Query completed');
connPool.close();
console.log("recordSet=");
console.dir(recordSet);
//return recordset;
}).catch(function (queryErr) {
console.log('Error in DB query: ' + queryErr);
//return queryErr;
});
}).catch(function (connErr) {
console.log('Error in DB connection: ' + connErr);
//return connErr;
*/
});
console.log('fall thru 1');
}
Result:
Connected - starting query
SQL Query = select student_firstname, student_lastname from students where student_id = 31
error400=ReferenceError: recordSet is not defined
empty test=0
You seem to be mixing Promises and Callbacks in a way that's making everything a little more confusing that it needs to me. The general pattern with promise is to return them and then call then which will give you the return value from the resolved promise. And remember that then() returns a promise as well, that's how you can chain them.
You can just return the Promise returned by connPool.connect() from your function. And then the called can call then() and catch() on it. The errors will float up to the final catch().
I don't have a connection for sql so I can't test this, but off the top of my head the idea is something like this (simplified for clarity):
const sql = require('mssql');
var connPool = new sql.ConnectionPool(config);
function getStudent(studentId) {
nullResult = {};
return connPool.connect() //return the promise from your function
.then (function() {
console.log('Connected - starting query');
var request = new sql.Request(connPool);
var sqlQuery = 'select student_firstname, student_lastname from students where student_id = ' + studentId;
connPool.close();
return request.query(sqlQuery) // returns the promise from request
})
}
app.get('/student', function(request, response){
getStudent(lookupStudentId)
.then(function(recordSet){
// recordSet should be promise resolution of request.query
})
.catch(function(err) {
// catch errors here
})
})
Here's a paired down example that helps show the promise chaining in action with a simple async promise mock that returns what you send it. You can uncomment the throw()s to see how errors bubble up:
function getStudent(val) {
return async(val)
.then (function(v) {
// console.log("In outer with, ", v)
return async("processes value: " + v)
})
.then(function (value) {
//console.log("Inside value got: ", value)
//throw("Some Error")
return async("processed again: " + value)
})
}
getStudent("Start_Value")
.then((value) => {
console.log("Final return: ",value)
})
.catch((err) => console.log("whoops err: ", err))
function async(p) {
return new Promise((resolve, reject) => {
//throw("async error")
setTimeout(() => {
resolve( "FROM ASYNC: " + p )
}, 500)
})
}
your callback concept is ok, you can also create your own promise in getStudent function and return it which will make your code more readable. Issue is with response.status(0).send(result); there is no such status exists with xhr calls or it will generate an error. Here, you can get some useful status with their global acceptable usage http://www.restapitutorial.com/httpstatuscodes.html
I would like to make http call synchronously using Q Promises, I have 100 students that I need for each of them to take some data from another platform and to do that I was trying via Q Promises but it does not seem like it is doing synchronously.
How do I make sure that another call is not being made once one is finished with parsing it's response and insertion into mongodb:
my code so far looks like this:
var startDate = new Date("February 20, 2016 00:00:00"); //Start from February
var from = new Date(startDate).getTime() / 1000;
startDate.setDate(startDate.getDate() + 30);
var to = new Date(startDate).getTime() / 1000;
iterateThruAllStudents(from, to);
function iterateThruAllStudents(from, to) {
Student.find({status: 'student'})
.populate('user')
.exec(function (err, students) {
if (err) {
throw err;
}
async.eachSeries(students, function iteratee(student, callback) {
if (student.worksnap.user != null) {
var worksnapOptions = {
hostname: 'worksnaps.com',
path: '/api/projects/' + project_id + '/time_entries.xml?user_ids=' + student.worksnap.user.user_id + '&from_timestamp=' + from + '&to_timestamp=' + to,
headers: {
'Authorization': 'Basic xxxx='
},
method: 'GET'
};
promisedRequest(worksnapOptions)
.then(function (response) { //callback invoked on deferred.resolve
parser.parseString(response, function (err, results) {
var json_string = JSON.stringify(results.time_entries);
var timeEntries = JSON.parse(json_string);
_.forEach(timeEntries, function (timeEntry) {
_.forEach(timeEntry, function (item) {
saveTimeEntry(item);
});
});
});
callback();
}, function (newsError) { //callback invoked on deferred.reject
console.log(newsError);
});
}
});
function saveTimeEntry(item) {
Student.findOne({
'worksnap.user.user_id': item.user_id[0]
})
.populate('user')
.exec(function (err, student) {
if (err) {
throw err;
}
student.timeEntries.push(item);
student.save(function (err) {
if (err) {
console.log(err);
} else {
console.log('item inserted...');
}
});
});
}
function promisedRequest(requestOptions) {
//create a deferred object from Q
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var deferred = Q.defer();
var req = http.request(requestOptions, function (response) {
//set the response encoding to parse json string
response.setEncoding('utf8');
var responseData = '';
//append data to responseData variable on the 'data' event emission
response.on('data', function (data) {
responseData += data;
});
//listen to the 'end' event
response.on('end', function () {
//resolve the deferred object with the response
console.log('http call finished');
deferred.resolve(responseData);
});
});
//listen to the 'error' event
req.on('error', function (err) {
//if an error occurs reject the deferred
deferred.reject(err);
});
req.end();
//we are returning a promise object
//if we returned the deferred object
//deferred object reject and resolve could potentially be modified
//violating the expected behavior of this function
return deferred.promise;
}
Anyone could tell me what do I need to do to achieve such things?
Is it also possible so that I know when all of the http calls are finished and the insertion is done for all...
I would abandon your current approach and use the npm module request-promise.
https://www.npmjs.com/package/request-promise
It's very popular and mature.
rp('http://your/url1').then(function (response1) {
// response1 access here
return rp('http://your/url2')
}).then(function (response2) {
// response2 access here
return rp('http://your/url3')
}).then(function (response3) {
// response3 access here
}).catch(function (err) {
});
Now you just need to convert this to some kind of iteration for the 100 requests you want and the job will be done.
I would use the promises of angularJS to fill data to a grid. I'd like to load data "row by row" as soon as the nodeJS's server, on which use the module "mssql" with the "stream" enabled, back to client every single line from the DB.
On the client side I use these functions:
function asyncGreet() {
var deferred = $q.defer();
var _url = 'http://localhost:1212/test';
$http.get(_url).
then(function(result) {
deferred.resolve(result);
}, function(error) {
deferred.reject(error);
}, function(value) {
deferred.notify(value); //<<-- In "value" I would like to get every single row
});
return deferred.promise;
}
$scope.btnTest = function () {
var promise = asyncGreet();
promise.then(function(res) {
console.log('Success: ' + res.data + "\n");
}, function(reason) {
console.log('Failed: ' + reason);
}, function(update) {
console.log('Got notification: ' + update); //<<--
});
};
On nodeJS server those:
app.get('/test', function (req, res) {
//sql for test
var _query = 'select top 50 * from tb_test';
var sql = require('mssql');
var connection;
var config = {
user: 'testUser',
password: '12345',
server: 'localhost\\test',
database: 'testDB',
stream: true
};
connection = new sql.Connection(config, function (err) {
var request = new sql.Request(connection);
request.query(_query);
request.on('recordset', function(columns) {
// Emitted once for each recordset in a query
//res.send(columns);
});
request.on('row', function(row) {
res.write(JSON.stringify(row)); //<<-- I would like intercept this event on client side
// and get the result in my angularJS function on deferred.notify
});
request.on('error', function(err) {
// May be emitted multiple times
console.error(err)
});
request.on('done', function(returnValue) {
// Always emitted as the last one
res.end('DONE');
});
});
});
Anyone can help me with this?
Thanks!
I'm done it using socket.io :)
On angularJS side:
// count the row for test only
$scope.count = 0;
$scope.prova = function () {
mySocket.emit('getTableByRow', {});
mySocket.on('resRow', function (data) {
if (data.event == 'ROW') {
$scope.count += 1;
}else {
$scope.count += " !!DONE!! ";
}
});
};
On NodeJS side:
[ ... connection with DB ... ]
io.on('connection', function (socket) {
socket.on('getTableByRow', function (data) {
_getTableByRow(socket, data);
});
});
_getTableByRow function:
var _getTableByRow = function (socket, data) {
var _query = 'select top 50 * from tb_test';
request.query(_query);
request.on('row', function(row) {
// return only the ids for test
socket.emit('resRow', {event: 'ROW', data: row.id.toString()});
});
request.on('done', function(returnValue) {
socket.emit('resRow', {event: 'DONE'});
});
request.on('recordset', function(columns) {
console.log(columns);
});
request.on('error', function(err) {
socket.emit('resRow', {event: 'ERROR', data: err});
});
}
In this way, as soon as one row is read from the DB, it is immediately sent to the client :)
I'm using the mssql module to connect to a sql server database using node. Bluebird has a feature that's similar to resource management in c#. It has a 'using' method to avoid having to use try/catch/finall to dispose of the resources. They have examples for pg and mysql, but they don't have an example for mssql which doesn't create a connection the same way as pg and mysql. Here's an example of how to use it:
using(getConnection(),
fs.readFileAsync("file.sql", "utf8"), function(connection, fileContents) {
return connection.query(fileContents);
}).then(function() {
console.log("query successful and connection closed");
});
But to be able to use this method, you need to create a connection method which describes how to close the connection. Here's an example for pg:
function getSqlConnection(connectionString) {
var close;
return pg.connectAsync(connectionString).spread(function(client, done) {
close = done;
return client;
}).disposer(function(client) {
if (close) close(client);
});
}
The problem I'm having with mssql module is that the connect method doesn't return a connection object like pg or even the mysql module. Has anyone been able to do this with mssql?
Update 1:
Here's how I made the transaction disposer:
function getTransaction(connection) {
return new Promise(function(resolve, reject) {
var tx = sql.Transaction(connection);
tx.beginAsync().then(function(err) {
if(err) {
tx = null;
return reject(err);
}
return resolve(tx);
});
}).disposer(function(tx, promise) {
if(promise.isFulfilled()) {
return tx.commitAsync();
}
else {
return tx.rollbackAsync();
}
});
}
It seems to be working, but not sure if this is efficient. Now I need to figure out how to catch errors on a query.
This is how I'm doing a transaction:
using(getConnection(), function(connection) {
return using(getTransaction(connection), function(tx) {
return query(queryString, tx).then(function() {
console.log('first query in transaction completed.');
console.log('starting second query in transaction.');
return query(anotherQueryString, tx);
});
});
});
If I tag a single catch to the outer 'using', will that catch all errors from the whole transaction?
Good question, mssql has really tricky API (constructors taking callbacks!) so this is good addition to the documentation.
var Promise = require("bluebird");
var sql = Promise.promisifyAll(require("mssql"));
global.using = Promise.using;
function getConnection(config) {
var connection;
return new Promise(function(resolve, reject)
connection = new sql.Connection(config, function(err) {
if (err) {
connection = null;
return reject(err);
}
resolve(connection);
});
}).disposer(function() {
if (connection) connection.close();
});
}
var config = {
user: '...',
password: '...',
server: 'localhost',
database: '...',
};
using(getConnection(config), function(connection) {
var request = new sql.Request(connection);
return request.queryAsync("select 1 as number").then(function(recordSet) {
console.log("got record set", recordSet);
return request.queryAsync("select 10 as number");
});
}).then(function(recordSet) {
console.log("got record set", recordSet);
})
To use the transaction, try implementing getTransaction like:
function getTransaction(connection) {
var tx = new sql.Transaction(connection);
return tx.beginAsync().thenReturn(tx).disposer(function(tx, promise) {
return promise.isFulfilled() ? tx.commitAsync() : tx.rollbackAsync();
});
}
And using it like:
using(getConnection(), function(connection) {
return using(getTransaction(connection), function(tx) {
var request = new sql.Request(tx);
return request.queryAsync("INSERT 1...").then(function() {
return request.queryAsync("INSERT 2...");
}).then(function() {
return request.queryAsync("INSERT 3...");
});
});
});
Error handling:
using(getConnection(), function(connection) {
return using(getTransaction(connection), function(tx) {
var request = new sql.Request(tx);
return request.queryAsync("INSERT...");
});
}).catch(sql.TransactionError, function(e) {
console.log("transaction failed", e);
}).catch(sql.ConnectionError, function(e) {
console.log("connection failed", e);
}).catch(sql.RequestError, function(e) {
console.log("invalid query", e);
});
Is there a better way to call the same function from the client and another node js module. Without having 2 separate functions. The only difference method the value is returned?
exports.getFiles = function(req,res){
var globPattern = req.body.globPattern;
var globOptions =req.body.globOptions;
glob(globPattern, globOptions, function (err, files) {
if(err)
{
res.status(400);
winston.log('error', err);
return res.send({success:false,reason: err});
}
res.send({success:true,data:files});
});
};
exports.getFilesFunc = function(payload){
var deferred = q.defer();
var globPattern = payload.globPattern;
var globOptions = payload.globOptions;
glob(globPattern, globOptions, function (err, files) {
if(err)
deferred.resolve({success:false,reason: err});
deferred.resolve({success:true,data: files});
});
return deferred.promise;
};
You can basically call getFilesFunc from getFiles:
exports.getFilesFunc = function(payload){
return Q.nfcall(glob, payload.globPattern, payload.globOptions);
};
exports.getFiles = function(req,res){
this.getFilesFunct(req.body).then(function(files) {
res.send({success:true, data:files});
}, function(err) {
res.status(400);
winston.log('error', err);
return res.send({success:false, reason: err});
});
};
Since you're using Promises, you can shorten your code to the following:
var Q = require('q'),
glob = Q.denodeify(require('glob'));
exports.getFiles = function(pattern, options) {
return glob(pattern, options);
};
Then in your controller, or wherever you're calling the function you'd control what you're going to do with your retured data:
module
.getfiles(req.body.globPattern, req.body.globOptions)
//or .getFiles(payload.globPattern, payload.globOptions)
.then(function (files) {
}, function (error) {
});