node.js sails.js waterline bluebird .then and .spread - node.js

I am trying to figure out how promises work in sails, and have been successful passing data from waterline queries via .then, but have not been able to utilize .spread. I am getting a function not defined error. Any suggestions how the first section of code can be improved to work?
//results in error
Promise.all([Xyz.find(), Abc.find()]).spread(function (someOtherResult, yetAnotherResult) {
console.log(someOtherResult)
}).catch(function (err) {
console.log(err);
})
The following work but would be either trickier to extract data from, or require excessively long nested .then clauses:
Promise.all([Xyz.find(), Abc.find()]).then(function (results) {
console.log(results[0][1]);
console.log(results[0].length);
})
Abc.find().then(function (foundAbcs) {
Promise.all(Xyz.find().then(function (foundXyzs) {
console.log(foundAbcs);
console.log(foundXyzs);
// additional syncranouse logic with Abc and Xyz
}))
})

Okay, very simple mistake, I didn't realize I needed:
var Promise = require('bluebird');
prior to module.exports in sails.js .11, problem solved.

Related

Using async-await to execute a task AFTER dropping a collection from mongodb database

I am trying to use async await to execute an http request before executing some other code.
More precisely, I would like to drop a collection in my mongodb database, before executing some others tasks. Here's what I did:
app.component.ts:
async deleteRiskRatingData2() {
await this.saveInformationService
.deleteRiskRatingInformation()
.subscribe((data: string) => {
console.log('Deleting risk Rating');
console.log(this.riskRatingTable);
});
console.log('TASKS TO BE EXECUTED AFTER DROPIING COLLECTION');
}
save-information.service.ts
deleteRiskRatingInformation() {
console.log('INIDE deleteRiskRatingInformation INSIDE SAVE-INFORMATION.SERVICE');
return this.http.get(`${this.uri}/dropRiskRatingCollection`);
}
In the backend:
server.js
router.route('/dropRiskRatingCollection').get((req, res) => {
RiskRating.remove({},(err) => {
if (err)
console.log(err);
else
res.json("Risk Rating Collection has been dropped!");
});
});
And this is what happens:
I though my implementation of Async/Await should allow me to execute the:
console.log('TASKS TO BE EXECUTED AFTER DROPPING COLLECTION');
After the dropping of the collection request has been executed. But that didn't happen as you see. And I really don't understand why.
Any idea why is this happening? Is my logic flawed somewhere? And how can I achieve my goal?
Thank you!
async-await work only with Promises. You're try them with Observables. That won't work. Observables have an API that let's you convert them into Promises though. You can call a toPromise method on them in order to do that.
Try this:
async deleteRiskRatingData2() {
const data = await this.saveInformationService.deleteRiskRatingInformation().toPromise();
console.log('Deleting risk Rating');
console.log(this.riskRatingTable);
console.log('TASKS TO BE EXECUTED AFTER DROPIING COLLECTION');
}
NOTE: It's fine if you're trying this just for the sake of testing it. But I think you should not really switch back to promises just to use async-await, to make your code look synchronous.

Mongodb testing in Node

I have been using Mocha for testing in Node because it seems to be what most people use. I am also using MongoDB to store my data and since my server is a simple API server pretty much all of my methods are just plain database queries which I am trying to test with Mocha.
Now the Issue that I am facing is that (besides the fact that it seems to be quite hard to test async functions in general) I can not seem to find a proper way to test for a mongoDB excpetion.
it('Should not create account', async (done) => {
try {
await createAccountDB(user);
await createAccountDB(user);
} catch (err) {
assert.notEqual(err, null);
console.log(err);
}
finally {
done();
}
});
});
What I am trying here is to create an account for a User (Basically just saving the Object to the db) and then creating the same account again which should result in a duplicate key error.
Now, this doesn't work and as far as I can tell that is because i have defined both async and done. The reason why I did this is, if I don't define async I would need to have a whole bunch of .then and .catches which would make the code look horrible but if I don't include then done() in the finally block, my test seems to never even make it to the catch block.
Is there any way to write tests like these in Mocha which don't make your code look absouletly horrible?
Since you're already using the async/await model you don't necessarily need the done callback for the test case. Certain versions of mocha will warn you when you have more than one means of indicating test completion. Try this:
it('should not create an account', async function() {
try {
await createAccountDB(user);
await createAccountDB(user);
throw new Error('Did not reject a duplicate account');
} catch (err) {
assert.notEqual(err, null);
// any other assertions here
}
});
The error thrown in the try/catch block is very important - without it, the test will still pass even if the error is not thrown.

node js mysqlquery attempting to add .then

Currently I'm working on a project in node js. Specifically I'm using soem boiler plate for adobe CEP which allows you to run some js in a panel in their programs. In the code there is the following code that works fine.
mysqlConn.query(query, function(err, result) {
do something with error and result})
When this is execute it gives me an error or result depending on if there is data or there was a problem etc. What I need to do is to run another function after this executes and gives me the result. My knowledge of promises is limited (even thou I've read extensively on it and done tons of tutorials). In my limited knowledge I assume mysqlConn.query returns a promise. So I was assuming I can just do this:
mysqlConn.query(query, function(err, result) {
do something with error and result})
.then(console.log('anything here?'));
This logs to the console 'anything here?' but it also gives me this error in the console.
Uncaught TypeError: mysqlConn.query(...).then is not a function
Any idea what I'm doing wrong or how I can achieve the desired results?
This indicates the mysqlConn.query method does not return a Promise
Instead, you will need to "promisify" the method so it can be changed with .then() and friends:
const myFunc = new Promise((resolve, reject) => {
return mysqlConn.query(query, function(err, result) {
if (err) reject(err);
return resolve(result);
});
});
Now we have myFunc, a Promise-based interface wrapping the callback-basked query function. We can use it like so:
return myFunc()
.then((result) => { ... }) // result will be the result of the query
.catch((err) => { .. } ) // err from the query as well
This can also be achieved in a slightly more involved way through other tools, but I highly recommend you understand this example first.

How to return promise to the router callback in NodeJS/ExpressJS

I am new to nodejs/expressjs and mongodb. I am trying to create an API that exposes data to my mobile app that I am trying to build using Ionic framework.
I have a route setup like this
router.get('/api/jobs', (req, res) => {
JobModel.getAllJobsAsync().then((jobs) => res.json(jobs)); //IS THIS THe CORRECT WAY?
});
I have a function in my model that reads data from Mongodb. I am using the Bluebird promise library to convert my model functions to return promises.
const JobModel = Promise.promisifyAll(require('../models/Job'));
My function in the model
static getAllJobs(cb) {
MongoClient.connectAsync(utils.getConnectionString()).then((db) => {
const jobs = db.collection('jobs');
jobs.find().toArray((err, jobs) => {
if(err) {
return cb(err);
}
return cb(null, jobs);
});
});
}
The promisifyAll(myModule) converts this function to return a promise.
What I am not sure is,
If this is the correct approach for returning data to the route callback function from my model?
Is this efficient?
Using promisifyAll is slow? Since it loops through all functions in the module and creates a copy of the function with Async as suffix that now returns a promise. When does it actually run? This is a more generic question related to node require statements. See next point.
When do all require statements run? When I start the nodejs server? Or when I make a call to the api?
Your basic structure is more-or-less correct, although your use of Promise.promisifyAll seems awkward to me. The basic issue for me (and it's not really a problem - your code looks like it will work) is that you're mixing and matching promise-based and callback-based asynchronous code. Which, as I said, should still work, but I would prefer to stick to one as much as possible.
If your model class is your code (and not some library written by someone else), you could easily rewrite it to use promises directly, instead of writing it for callbacks and then using Promise.promisifyAll to wrap it.
Here's how I would approach the getAllJobs method:
static getAllJobs() {
// connect to the Mongo server
return MongoClient.connectAsync(utils.getConnectionString())
// ...then do something with the collection
.then((db) => {
// get the collection of jobs
const jobs = db.collection('jobs');
// I'm not that familiar with Mongo - I'm going to assume that
// the call to `jobs.find().toArray()` is asynchronous and only
// available in the "callback flavored" form.
// returning a new Promise here (in the `then` block) allows you
// to add the results of the asynchronous call to the chain of
// `then` handlers. The promise will be resolved (or rejected)
// when the results of the `job().find().toArray()` method are
// known
return new Promise((resolve, reject) => {
jobs.find().toArray((err, jobs) => {
if(err) {
reject(err);
}
resolve(jobs);
});
});
});
}
This version of getAllJobs returns a promise which you can chain then and catch handlers to. For example:
JobModel.getAllJobs()
.then((jobs) => {
// this is the object passed into the `resolve` call in the callback
// above. Do something interesting with it, like
res.json(jobs);
})
.catch((err) => {
// this is the error passed into the call to `reject` above
});
Admittedly, this is very similar to the code you have above. The only difference is that I dispensed with the use of Promise.promisifyAll - if you're writing the code yourself & you want to use promises, then do it yourself.
One important note: it's a good idea to include a catch handler. If you don't, your error will be swallowed up and disappear, and you'll be left wondering why your code is not working. Even if you don't think you'll need it, just write a catch handler that dumps it to console.log. You'll be glad you did!

Bluebird Warning : a promise was created in a handler but was not returned from it

I have an array of filenames which I iterate using this async module for node.
async.eachSeries(imageStore, function(imageDetails,callback){
mongoMan.updateCollection('imageStore',imageDetails,{_id: imageDetails.fileName}).then(function(res){
return callback(null, res);
}).catch(function(err){
logger.error(err);
return callback(err);
});
},function(err){
callback(null);
});
The updateCollection() function is like this:
exports.updateCollection = function(collection, values, condArr){
var result = imageStores.updateAsync(condArr, values, { upsert: true }).then(function(res){
return new Promise.resolve(res);
}).catch(function(err){
logger.error(err);
});
return new Promise.resolve(result);
}
This code works well, updates DB and everything. But I am still unable to resolve the warning that bluebird throws:
Warning: a promise was created in a handler but was not returned from it
at Object.exports.updateCollection (/home/swateek/Documents/codebase/poc/apps/webapp/server/components/mongodb/mongoConn.js:46:22)
at /home/swateek/Documents/codebase/poc/apps/webapp/server/components/imageStore.js:72:24
at /home/swateek/Documents/codebase/poc/apps/webapp/node_modules/async/lib/async.js:181:20
at iterate (/home/swateek/Documents/codebase/poc/apps/webapp/node_modules/async/lib/async.js:262:13)
Have looked up solutionhere but that ain't convincing enough in my case. Atleast is there a way to turn off the warning?
UPDATE
Please check the correct answer below, and here's how my calling function looks like:
function(callback){// post imageStore data to DB
async.eachSeries(imageStore, function(imageDetails,callback){
mongoMan.updateCollection('imageStore',imageDetails,{_id: imageDetails.fileName}).catch(function(err){
logger.error(err);
return callback(err);
});
return callback(null);
},function(err){
callback(null);
});
}
You are asking for trouble and throwing away programming advantages if you try to mix async library callbacks with promises. Pick one structure or the other and use it everywhere. Personally, I'd suggest you move to promises and just "promisify" functions that currently work with callbacks. If you're using the Bluebird Promise library, then it has Promise.promisify() and Promise.promisifyAll() which make it pretty easy to promisify things that use the standard node.js async callback so you can just control everything with Promise logic.
Now, onto your specific issue. That error means that you are creating promises within a .then() handler, but those promises are not returned or chained onto any previous promise. Thus, they are completely independent from your other chain. This is usually a bug (thus the reason for the warning). The only time one might actually want to do that is you have some fire and forget operation that you don't want the outer promise to wait for and you aren't tracking errors on which is not what you have here.
Change to this:
exports.updateCollection = function(collection, values, condArr){
return imageStores.updateAsync(condArr, values, { upsert: true}).catch(function(err){
logger.error(err);
// rethrow error so the caller will see the rejection
throw err;
});
}
Changes:
Return the main promise rather than creating a new promise.
No need for return new Promise.resolve(res); as res is already returned from that promise so you can remove that whole .then() handler as it isn't doing anything.
No need for return new Promise.resolve(result); as you can just return the earlier promise directly.
FYI, though you don't need it here at all, Promise.resolve() can be called directly without new.
You have some issues here:
var result = imageStores.updateAsync(condArr, values, { upsert: true }).then(function(res){
return new Promise.resolve(res);
}).catch(function(err){
logger.error(err);
});
return new Promise.resolve(result);
Firstly: Promise.resolve is not a constructor so it shouldn't be used with new. Secondly: there is no point calling Promise.resolve( result ) at all, just return result which is already a Promise. The intermediate then is pointless as well. You can reduce that code to:
return imageStores.updateAsync(condArr, values, { upsert: true })
.catch( function(err){
logger.error(err);
} )
;

Resources