I am trying to write a function in node js that will run a SQL query using mssql and return a promise. For some reason it gets to the line
console.log("got to the run proc functions");
but won't run any code after that. Any help with this issue would be greatly appreciated.
runProc: function (params) {
var sql = require("mssql");
sql.Promise = require('promise');
return new Promise((resolve, reject) => {
var dbConfig = {
server:"ip",
database: "db",
user:"user",
password: "pw"
}
console.log("got to the run proc functions");
var keys = Object.keys(params);
sql.connect(dbConfig).then(pool => {
console.log("got connected");
const request = pool.request()
for (var i = 0; i < keys.length; i++) {
if (keys[i].substring(0,6)=="Common") {
request.input(keys[i],sql.Bit,params[keys[i]]);
console.log("set the bit parameters");
}
else {
request.input(keys[i],params[keys[i]]);
console.log("set the other parameters");
}
}
request.execute("storedprocedure")
return request;
}).then(result => {
resolve(result)
}).catch(err => {
reject(Error(err))
});
sql.close();
});
}
Look where you call sql.close(). i.e. it's being called before request=pool.request etc! So, that would fail for a start.
Also, you are returning request (which isn't a promise) rather than request.execute() (which is a promise) so, the promise would resolve before execute completes
And finally, no need to wrap a promise inside a Promise constructor (not that this breaks your code, but there's no need for it)
Given all that, your code is
runProc: function (params) {
var sql = require("mssql");
sql.Promise = require('promise');
// removed new Promise constructor as sql.connect already returns a promise we can chain to and return
var dbConfig = {
server:"ip",
database: "db",
user:"user",
password: "pw"
}
console.log("got to the run proc functions");
var keys = Object.keys(params);
return sql.connect(dbConfig)
.then(pool => {
console.log("got connected");
const request = pool.request()
for (var i = 0; i < keys.length; i++) {
if (keys[i].substring(0,6)=="Common") {
request.input(keys[i],sql.Bit,params[keys[i]]);
console.log("set the bit parameters");
}
else {
request.input(keys[i],params[keys[i]]);
console.log("set the other parameters");
}
}
// need to return the result of execute, not the request object
return request.execute("storedprocedure")
})
.catch(err => throw new Error(err))
// use Promise#finally - available in all good promise libraries
.finally(sql.close); // sql.close AFTER we're done with it, not before
}
Using the sparkly new async/await promise sugar simplifies the code even more
runProc: async function (params) {
var sql = require("mssql");
sql.Promise = require('promise');
var dbConfig = {
server:"ip",
database: "db",
user:"user",
password: "pw"
}
console.log("got to the run proc functions");
var keys = Object.keys(params);
try {
const pool = await sql.connect(dbConfig);
const request = pool.request();
for (var i = 0; i < keys.length; i++) {
if (keys[i].substring(0,6)=="Common") {
request.input(keys[i],sql.Bit,params[keys[i]]);
console.log("set the bit parameters");
}
else {
request.input(keys[i],params[keys[i]]);
console.log("set the other parameters");
}
}
// need to return the result of execute, not the request object
return await request.execute("storedprocedure");
} catch (err) {
throw new Error(err);
} finally {
sql.close();
}
}
Related
In my angular app, I have 2 methods save() and saveTriggers(). saveTriggers() updates all records by calling a web service (C#). I want to make sure that a block of code is executed after all records are updated in saveTriggers() and control is returned to save(). I believe I need to pass something from the saveTriggers() to make finally block execute. I tried various things, nothing works. Using .then() also gives the same error. I am not that good at JS. Can you please guide me.
vm.updatedTriggers = []; // IDs are pushed in
vm.saveTriggers = function () {
if (vm.updatedTriggers.length === 0) {
vm.close();
} else {
vm.saving = true;
vm.save()
.finally(function () { // ERROR - Cannot read property 'finally' of undefined
console.log("Saved all. Closing..."); // Never REACHES here
vm.saving = false;
vm.updated = true;
$uibModalInstance.close(true);
});
}
};
vm.save = function () {
//vm.saving = true;
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
target: trigger.target,
targetInfo: vm.targetData,
event: trigger.event,
eventQuantity: trigger.eventQuantity,
eventQuantityExtra: trigger.eventQuantityExtra
};
rpmService.editDeviceTrigger(input);
/*.finally(function () {
console.log("Updated event"); // Reaches here
vm.updated = true;
return Promise.resolve(2);
});*/ // Commenting this also doesn't help
}
return Promise.resolve(2);
};
rpmService.editDeviceTrigger(input)
public async Task EditDeviceTrigger(EditDeviceTriggerInput input) {
// calls other methods with await
// Doesn't return anything
}
EDIT: Updated Code: I got rid of the error, but the output is not is expected series.
vm.saveTriggers = function () {
vm.saving = true;
vm.save().then
(function success() {
console.log("Returned Result ");
console.log("Saved all. Closing..."); // These lines are executed before the event is upated
vm.saving = false;
$uibModalInstance.close(true);
});
};
vm.save = function () {
var deferred = $q.defer();
for (var i = 0; i < vm.updatedTriggers.length; i++) {
var trigger = vm.triggers.find(t => t.id === vm.updatedTriggers[i]);
var input = {
id: trigger.id,
....
};
rpmService.editDeviceTrigger(input)
.finally(function () {
console.log("Updated event"); // Successfully updates all events
vm.updated = true;
});
}
deferred.resolve();
return deferred.promise;
};
OUTPUT:
Returned Result
Saved all. Closing...
Updated event
EXPECTED OUTPUT:
Updated event
Returned Result
Saved all. Closing...
Thanks.
Usually you dont need $q.defer-related things, but u can do same using it if u want.
Here I guess you just need to collect all your save promises and return new resulting one using $q.all:
vm.save = function () {
const myAwesomePromises = []
for (var i = 0; i < vm.updatedTriggers.length; i++) {
...
const savePromise = rpmService.editDeviceTrigger(input);
savePromise.finally(() => console.log('edit device finally'));// <-- not sure u need this
myAwesomePromises.push(savePromise);
}
return $q.all(myAwesomePromises).finally(() => console.log('All edit device finally'));
};
In the code below, when I run in debug mode with a break-point at this line: content.push(data.Body.toString()); I can see that data is inserted to the content array.
However when I run the code normally, content comes back empty.
How can I get it to populate the array for downstream use?
var params = { Bucket: "thebucket", Prefix: "theprefix/" }
var content = [];
function getS3Data()
{
var s3 = new aws.S3();
s3.listObjects(params, function (err, data)
{
if (err) throw err; // an error occurred
else
{
var i;
for (i = 0; i < data.Contents.length; i++)
{
var currentValue = data.Contents[i];
if(currentValue.Key.endsWith(params.Prefix) == false)
{
var goParams = { Bucket: params.Bucket, Key: currentValue.Key };
s3.getObject(goParams, function(err, data)
{
if (err) throw err; //error
content.push(data.Body.toString());
});
};
};
}//else
});//listObjects
}//getS3Data
getS3Data();
console.log(content); //prints empty here when run in non-debug.
The line:
console.log(content)
is being executed before the line:
content.push(data.Body.toString());
the function you are passing as a 2nd argument to s3.listObjects will be executed asynchronously. If you want to log out content you need to do it within the callback function meaning:
s3.listObjects(params, function (err, data) {
if (err) throw err;
else {
// ...
console.log(content)
}
});
A better approach would be to implement getS3Data with Promise so you can run code after the object listing is done for sure.
function getS3Data() {
return new Promise((resolve, reject) => {
if (err) {
reject(err)
} else {
const promises = []
for (const i = 0; i < data.Contents.length; i++) {
const currentValue = data.Contents[i];
if (currentValue.Key.endsWith(params.Prefix) == false) {
const goParams = { Bucket: params.Bucket, Key: currentValue.Key };
promises.push(new Promise((res, rej) => {
s3.getObject(goParams, function (err, data) {
if (err) {
rej(err); //error
} else {
res(data.Body.toString());
}
});
}));
}
}
Promise.all(promises).then(resolve);
}
});
}
getS3Data()
.then(result => { // this will actually be `content` from your code example
console.log(result);
}).catch(error => {
console.error(error);
})
Node.js' documentation has an example very similar to the problem you are experiencing:
Dangers of Mixing Blocking and Non-Blocking Code
The issue arises because the variable content is not set as soon as getS3Data has finished, because it is an asynchronous function. content will be set some time later. But your call to console.log(content); will execute immediately after getS3Data has finished, so at that point content has not been set yet.
You can test that by adding an extra log:
s3.getObject(goParams, function(err, data)
{
if (err) throw err; //error
content.push(data.Body.toString());
console.log("Content has been assigned");
});
And then change the bottom to:
getS3Data();
console.log("getS3Data has finished", content);
It's likely you'll get the messages in this order:
getS3Data has finished
Content has been assigned
I am attempting to stream the data from a procedure that returns multiple data sets using node and mssql. If using as a stand a long function it works, but I need it to return the dataset from the route i am using.
handler: function(request, reply) {
var inputValues = request.payload.inputParams;
var procName = request.params.procedureName;
var request = new sql.Request(mainsettings.connection);
request.stream = true;
var newGroup = [];
var count = 0;
var recordSetArr = [];
for(var key in inputValues) {
var currentParam = inputValues[key];
var paramType = getParamType(sql, currentParam.paramType);
try {
request.input(currentParam.paramName, paramType, currentParam.paramValue);
}
catch(err) {
console.error(err);
}
}
request.execute(procName);
request.on('recordset', function(columns) {
// Emitted once for each recordset in a query
count++;
if(count > 1) {
recordSetArr.push(newGroup);
}
newGroup = [];
});
request.on('row', function(row) {
// Emitted for each row in a recordset
newGroup.push(row);
});
request.on('error', function(err) {
// May be emitted multiple times
console.error(err);
});
request.on('done', function(returnValue) {
// Always emitted as the last one
return (recordSetArr);
});
}
I am getting the following error.
Debug: internal, implementation, error
Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (F:\FARA_API\node_modules\hapi\l
ib\toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
180406/194833.505, (1523044113505:MISOM-DEV-002:6976:jfod4ysa:10140) [error] mes
sage: handler method did not return a value, a promise, or throw an error, stack
: Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (F:\FARA_API\node_modules\hapi\l
ib\toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
Any suggestions on how I am supposed to return the data set properly?
Also tried the following
handler: function(request, reply) {
recordRouteCall("procedure");
var inputValues = request.payload.inputParams;
var procName = request.params.procedureName;
sql.connect(mainsettings.connection, err => {
var request = new sql.Request();
request.stream = true;
var newGroup = [];
var count = 0;
var recordSetArr = [];
for(var key in inputValues) {
var currentParam = inputValues[key];
var paramType = getParamType(sql, currentParam.paramType);
try {
request.input(currentParam.paramName, paramType, currentParam.paramValue);
}
catch(err) {
console.error(err);
}
}
request.execute(procName);
request.on('recordset', columns => {
// Emitted once for each recordset in a query
count++;
if(count > 1) {
recordSetArr.push(newGroup);
}
newGroup = [];
});
request.on('row', row => {
// Emitted for each row in a recordset
newGroup.push(row);
});
request.on('error', err => {
// May be emitted multiple times
console.error(err);
});
request.on('done', result => {
// Always emitted as the last one
console.log(recordSetArr);
return (recordSetArr);
});
});
sql.on('error', err => {
console.error(err);
return err;
});
}
I have a scenario in which there is 8 web api's called as :
#1
Sync Local DB from server DB (response will RETURN a List=> myList)
If (myList.Length > 0)
#1.1 Call web Api to Insert/Update Local DB
#2
Sync Server DB from Local DB (Request goes with a List=> myList)
If (myList.Length > 0)
#2.1 Call web Api to Insert/Update in Server DB (Response will RETURN a List=> newList)
If(newList.length > 0)
#2.2 Call web Api to Insert/Update in Local DB
I have two separate process For Head and Head Collection tables which synced with above process. So there is #3 and #4 scenario is also present.
I have call web api in the following manner...
syncHeadDataLogic();
syncHeadCollectionDataLogic();
I need that Head data should be synced first then HeadCollection data synced. But if there is no updated record for head then Head collection executed.
In my scenario my web apis called in any order but i need a order as I have described above. Kindly suggest me how I achieved this.
#Updated
//Sync Head
$scope.initializeController = function () {
if ($scope.online) {
//debugger;
syncHeadDataLogic();
syncHeadCollectionDataLogic();
}
};
function syncHeadDataLogic() {
HeadService.HeadSyncLocalDB(parseInt(localStorage.headRevision, 10), $scope.completeds, $scope.erroe);
};
$scope.SynServerDBCompleted = function (response) {
debugger;
$scope.HeadListForSync = response.HeadList;
var tempHeadCurrencyDetail = [];
if ($scope.HeadListForSync.length > 0) {
angular.forEach($scope.HeadListForSync, function (xx) {
xx.CurrencyId = xx.CurrencyServerId;
xx.Id = xx.HeadServerId;
angular.forEach(xx.HeadCurrencyDetail, function (yy) {
yy.CurrencyId = yy.CurrencyServerId;
yy.HeadId = xx.HeadServerId;
if (yy.Revision == -1)
tempHeadCurrencyDetail.push(yy);
});
xx.HeadCurrencyDetail = tempHeadCurrencyDetail;
});
var postData = { Revision: parseInt(localStorage.headRevision, 10), HeadList: $scope.HeadListForSync };
HeadService.SynServerDB(postData, $scope.completed, $scope.erroe);
}
else {
// alertsService.RenderSuccessMessage("There is no change in data after your last synchronization.");
}
};
$scope.requestErrorwer = function (response) {
debugger;
};
$scope.completed = function (response) {
debugger;
if (response.RevisionNo == localStorage.headRevision) {
syncHeadCollectionDataLogic();
// alertsService.RenderErrorMessage("There is newer version on the server. Please Sync from server first.", "MessageAlert");
}
else {
syncData(response);
}
};
$scope.completeds = function (response) {
debugger;
if (response.RevisionNo == localStorage.headRevision) {
syncHeadCollectionDataLogic();
// alertsService.RenderSuccessMessage("You are already working on the latest version", "MessageAlert");
}
else {
syncData(response);
}
//
var request = new Object();
HeadService.getAllHeadForRevision(request, $scope.SynServerDBCompleted, $scope.requestErrorwer);
};
$scope.erroe = function (response) {
debugger;
// alertsService.RenderErrorMessage("Data Synchronization Failed", "MessageAlert");
};
function syncData(data) {
debugger;
$scope.ReturnedRevisonNo = data.RevisionNo;
if (data.HeadList && data.HeadList.length > 0) {
var postData = { Revision: $scope.ReturnedRevisonNo, HeadList: data.HeadList, HeadRevision: $scope.ReturnedRevisonNo };
HeadService.AddUpdateHeadAfterSync(postData, $scope.cmpSync, $scope.Error);
}
else {
syncHeadCollectionDataLogic();
}
};
$scope.cmpSync = function (response) {
debugger;
localStorage.headRevision = $scope.ReturnedRevisonNo;;
alertsService.RenderSuccessMessage("The synchronization has been completed successfully.");
syncHeadCollectionDataLogic();
};
$scope.Error = function (response) {
debugger;
// alertsService.RenderErrorMessage(response.ReturnMessage);
// alertsService.SetValidationErrors($scope, response.ValidationErrors);
};
////////////Sync End
//Sync Head Collection
function syncHeadCollectionDataLogic() {
HeadService.HeadSyncLocalCollectionDB(parseInt(localStorage.headCollectionRevision, 10), $scope.completedCollections, $scope.erroeCollection);
};
$scope.SynServerDBCompletedCollection = function (response) {
$scope.HeadCollectionListForSync = response.HeadCollectionList;
if ($scope.HeadCollectionListForSync.length > 0) {
angular.forEach($scope.HeadCollectionListForSync, function (value, index) {
value.Id = value.HeadCollectionServerId;
angular.forEach(value.HeadCollectionDetails, function (v) {
v.CommittedCurrencyId = v.CommittedCurrencyServerId;
v.HeadId = v.HeadServerId;
v.WeightId = v.WeightServerId;
v.HeadCollectionId = value.HeadCollectionServerId; //change
angular.forEach(v.HeadCollectionAmountDetails, function (xx) {
xx.CurrencyId = xx.CurrencyServerId;
});
});
});
var postData = { Revision: parseInt(localStorage.headCollectionRevision, 10), HeadCollectionList: $scope.HeadCollectionListForSync };
HeadService.SynServerCollectionDB(postData, $scope.completedCollection, $scope.erroeCollection);
}
else {
// alertsService.RenderSuccessMessage("There is no change in data after your last synchronization.");
}
};
$scope.requestErrorwerCollection = function (response) {
};
$scope.completedCollection = function (response) {
if (response.RevisionNo == localStorage.headCollectionRevision) {
// alertsService.RenderErrorMessage("There is newer version on the server. Please Sync from server first.", "MessageAlert");
}
else {
syncDataCollection(response);
}
};
$scope.completedCollections = function (response) {
if (response.RevisionNo == localStorage.headCollectionRevision) {
// alertsService.RenderSuccessMessage("You are already working on the latest version", "MessageAlert");
}
else {
syncDataCollection(response);
}
var request = new Object();
HeadService.getAllHeadCollectionForRevision(request, $scope.SynServerDBCompletedCollection, $scope.requestErrorwerCollection);
};
$scope.erroeCollection = function (response) {
// alertsService.RenderErrorMessage("Data Synchronization Failed", "MessageAlert");
};
function syncDataCollection(data) {
$scope.ReturnedRevisonNo = data.RevisionNo;
if (data.HeadCollectionList && data.HeadCollectionList.length > 0) {
var postData = { Revision: $scope.ReturnedRevisonNo, HeadCollectionList: data.HeadCollectionList, HeadRevision: $scope.ReturnedRevisonNo };
HeadService.AddUpdateaHeadCollectionAfterSync(postData, $scope.cmpSyncCollection, $scope.ErrorCollection);
}
};
$scope.cmpSyncCollection = function (response) {
localStorage.headCollectionRevision = $scope.ReturnedRevisonNo;;
alertsService.RenderSuccessMessage("The synchronization has been completed successfully.");
$scope.initializeController();
};
$scope.ErrorCollection = function (response) {
// alertsService.RenderErrorMessage(response.ReturnMessage);
// alertsService.SetValidationErrors($scope, response.ValidationErrors);
}
//End
I need that Head data should be synced first then HeadCollection data synced. But if there is no updated record for head then Head collection executed.
What you need is chained promises. Try this (I'm giving you pseudocode for now):
HeadService.HeadData
|-----------------|
HeadCollection(headDataResult)
|------------------|
finalHandler(headCollectionResult)
|------------------|
HeadService.HeadData()
.then(HeadService.HeadCollection) // return or throw Err if headDataResult is empty
.then(finalHandler);
Here, the order of execution of the promises will be predictable, and sequential. Also, each promise will be returned the resolved value of the previous promise
AngularJS as you can see in the documentation here, uses Promises out of the box with the $http injectable. You can define a factory like so:
// Factory code
.factory("SampleFactory", function SampleFactory($http) {
var sampleFactoryObject = {};
sampleFactoryObject.getSomething = function() {
return $http.get('/someUrl');
}
sampleFactoryObject.getSomething.then(function resolveHandler(res) {
return res;
},
function rejectHandler(err) {
throw new Error(err);
});
sampleFactoryObject.getSomethingElse = function() {
return $http.get('/someOtherUrl');
}
sampleFactoryObject.getSomethingElse.then(function resolveHandler(res) {
return res;
},
function rejectHandler(err) {
throw new Error(err);
});
return sampleFactoryObject;
});
// Controller code
.controller('myController', function myController(SampleFactory) {
SampleFactory.getSomething()
.then(SampleFactory.getSomethingElse())
.then(finalHandler);
var finalHandler = function(resultOfGetSomethingElse) {
console.log(resultOfGetSomethingElse);
}
});
I have a registration dialog where when the user enters username and password I need to check the DB whether the user is present
or not. But when I am validation for the same my call does not hold back until I get the results from the server.
After searching for a while I got to know about callbacks. So I have added a call back inside this.isUser method.
And it is successful. But now doRegistration method is not synchronous with the isUser method.
How to make all my calls synchronous?
this.doRegistration = function(uname, pwd, confirmPwd) {
if(this.isUser(uname)) {
return "USER_EXISTS";
} else {
saveUser(uname, pwd);
return "SUCCESS";
}
};
this.isUser = function(username) {
var users = new Array();
getAllUsers('param', function(response) {
users = response;
console.log(users.length);
for(i = 0; i < users.length; i++) {
if(users[i].username === username) {
return true;
}
}
return false;
});
};
function getAllUsers(param, callback) {
loginFactory.AllUsers.query(function(response) {
if(response != undefined && response.length > 0) {
callback(response);
}
});
}
You may rewrite the code like following:
this.doRegistration = function(uname, pwd, confirmPwd, callBack) {
this.isUser(uname,function(flag) {
if(flag){
callBack("USER_EXISTS");
}
else {
saveUser(uname, pwd, function(err,result){
if(err){
callBack("SAVING_FAILED");
}
else {
callBack("SUCCESS");
}
});
}
});
};
this.isUser = function(username,callBack) {
var users = new Array();
getAllUsers('param', function(response) {
users = response;
console.log(users.length);
for(i = 0; i < users.length; i++) {
if(users[i].username === username) {
callBack(true);
}
}
callBack(false);
});
};
function saveUser(userName, pwd, callBack){
//code to save user
//chek for error in saving
if(err){
callBack(err,null)
}
else {
callBack(null, "success")
}
}
function getAllUsers(param, callback) {
loginFactory.AllUsers.query(function(response) {
if(response != undefined && response.length > 0) {
callback(response);
}
});
}
You may also define saveUser as a function with callback. Here it wont wait for saveUser method to complete.