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.
Related
Hello i really need help with this issue, my last console.log is execute BEFORE the for loop and i dont know how to fix it. I really need to have access at my array nbfilm after the for loop
Can someone help me?
What the console print : lien
client.db.query("SELECT name,id FROM film", function (err, result) {
if (err) throw err;
const catalog = new MessageEmbed()
.setTitle("Catalogue")
.setColor("#fcfe80")
.setFooter({text:"🍿 ・ PopFlix"})
let testresult =[]
let nbfilm =[]
for (let compteur of result){
testresult.push(compteur.id)
testresult.push(compteur.name)
}
console.log(testresult)
for (let compteur2 = 0; compteur2 < testresult.length; compteur2+=2){
client.db.query(`SELECT link FROM lien WHERE fid=${testresult[compteur2]}`, function (err,result) {
nbfilm.push(testresult[compteur2+1])
nbfilm.push(result.length)
console.log("nbfilm in for loop",nbfilm)
});
}
console.log("nbfilmAFTER",nbfilm)
});
The body of the loop delays execution. Due to the fact that javascript is an asynchronous i/o type operation language, it is common to return a promise. In other words, the code is executed as expected, but the result of the actions will be visible only after all pending tasks have been completed. In your case, adding async/await in code might help. Use it node docs
It looks like client.db.query is asynchroneous. JS here works as expected because it doesnt wait for the query to be finished before moving to the next line.
Its now a better practice to use async/await instead of callbacks. If you provide the package that you are using we can provide a code example.
client.db.query() is an asynchronous function and won't execute until after the console.log(nbfilm) is executed regardless of how fast that query actually runs.
I'd recommend using Promise.all(). You will also have to "promisify" the query() function. Pass everything you want to resolve(), and then concatenate them in the final line as below.
let promises = [];
for(....) {
promises.push(new Promise((resolve, reject) => {
client.db.query("select ... ", () => {
// all the same stuffs
resolve([testresult[compteru2+1], result.length]);
});
}))
}
Promise.all(promises)
.then((results) => results.reduce((p, c) => p.concat(c), []))
.then(data => console.log(data)); // do whatever you want with "data"
Here's a simplified demo:
https://codesandbox.io/s/promise-all-example-78r347
I am looking into solutions to call some C# code from within my NodeJS application. I came across EdgeJS, which seems to make this possible. However, I came across part of their code that is confusing to me, because I see an async keyword without an accompanying await. And while I'm more familiar with JS than C#, my understanding is that in BOTH you need to include an await with any async. This is the code in question, where some multi-line C# code is defined within backticks:
var edge = require('edge');
var helloWorld = edge.func(`
async (input) => {
return ".NET Welcomes " + input.ToString();
}
`);
helloWorld('JavaScript', function (error, result) {
if (error) throw error;
console.log(result);
});
Can someone explain how this is working, considering await doesn't appear anywhere?
I am building a small node.js website with a user interface that features a dropdown with a list of countries.
Previously the list of countries was hard coded in a json file that I would read:
exports.countries = require('./json/countries.json');
Then I realized I shouldn't hard code it like that when I can do a distinct query to the the list from the mongodb database.
db.collection.distinct('c', {}, function(err, data) {
// something
});
But then there's the question of how to extract the value of the data variable in that callback function. I discovered that this works:
db.collection.distinct('c', {}, function(err, data) {
if (err) {
throw Error('mongodb problem - unable to load distinct values');
} else {
exports.countries = data;
}
});
I am new to node.js and this seems fishy to me. Is this OK code? Is it better do this with generators or promises? If I wanted to use generators or promises to do this, how would I do that?
The end result where this is used is in a template. ref.countries is the actual list of countries using my fishy code. If I had a Promise instead of the list of countries, how would I change this code?
<% ref.countries.forEach(function(c) { -%>
<option value="<%= c %>">
<%= ref.isoCodes[c] -%>
</option>
<% }); -%>
I am using node v6.10.3.
Your export that you say "works" is impossible to use because the code that loads your module would have no idea when the exports.countries value has actually been set because it is set in an asynchronous call that finishes some indeterminate time in the future. In addition, you have no means of handling any error in that function.
The modern way of doing this would be to export a function that, when called, returns a promise that resolves to the data (or rejects with an error). The code loading your module, then calls that exported function, gets the promise, uses .then() on the promise and uses the data in the .then() handler. That could look something like this:
function getCountries() {
return new Promise(function(resolve, reject) {
db.collection.distinct('c', {}, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
}
}
module.exports.getCountries = getCountries;
The caller would then do something like this:
const myModule = require('myModule');
myModule.getCountries().then(function(countries) {
console.log(countries);
// use country data here
}).catch(function(err) {
// deal with error here
});
Most databases for node.js these days have some level of promise support built in so you often don't have to create your own promise wrapper around your DB functions like was shown above, but rather can use a promise directly returned from the DB engine. How that works is specific to the particular database/version you are using.
If you are using the list of countries in a template rendering operation, then you will have to fetch the list of countries (and any other data needed for the template rendering) and only call res.render() when all the data has been successfully retrieved. This probably also leads to what you should do when there's an error retrieving the necessary data. In that case, you would typically respond with a 5xx error code for the page request and may want to render some sort of error page that informs the end-user about the error.
I am using Node 6.10 so I don't have async and await but if I did they would help me here:
https://developers.google.com/web/fundamentals/getting-started/primers/async-functions
Instead I can use the asyncawait library:
https://github.com/yortus/asyncawait
Code looks like this:
var async = require('asyncawait/async');
var await = require('asyncawait/await');
const db = require('_/db');
function getDistinctValues(key) {
return new Promise((resolve, reject) => {
db.collection.distinct(key, {}, function(err, data) {
if (err) {
throw Error('mongodb problem - unable to load distinct values');
} else {
resolve(data);
}
});
});
};
async(function () {
exports.countries = await(getDistinctValues('c'));
exports.categories = await(getDistinctValues('w'));
})();
Now I can be sure ref.countries and ref.categories are available after this is loaded.
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!
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);
} )
;