i have this code for a statistic report over a db.
exports.calculate = function(req, res, next) {
models.Quiz.count()
.then(function(questions) {
statistics.questions = questions;
models.Comment.count().then(function(comments) {
statistics.comments = comments;
statistics.average_comments = (statistics.comments / statistics.questions).toFixed(2);
models.Quiz.findAll({
include: [{model: models.Comment}]})
.then(function(quizes) {
for (index in quizes) {
if (quizes[index].Comment.length) {
statistics.commented_questions++;
} else {statistics.no_commented++;}
};
})
})
})
.catch(function(error) {next(error)})
.finally(function() {next()});
};
It works properly until the SQL statement, but never makes the loop for, so i never can get
statistics.commented_questions
or
statistics.no_commented
Thank's in advanced!
When chaining promises together they need to know when the previous promise is either rejected or fulfilled. In your current code, the initial promise never returns a value/promise but instead calls an async function. The code essentially looks like this to the JS engine:
exports.calculate = function(req, res, next) {
models.Quiz.count()
.then(function(questions) {
statistics.questions = questions;
// ASYNC FUNCS THAT ARE NEVER RETURNED
// ...
// functions in JS without an explicit return statement return nothing (essentially `undefined`)
})
.catch(function(error) {
next(error)
})
.finally(function() {
next()
});
};
So, after the engine waits for the initial promise to be fulfilled/rejected it fires off another promise for an async operation that returns a promise but doesn't return it to the original promise chain. By default the original promise chain receives undefined which is then passed on to the next method in the chain. In this case it would be the finally method.
You might wonder why the second promise is still updating the information if it's not waiting for it. This is a race condition and essentially that promise is winning.
To properly chain the promises together you need to return the new promise to the old promise chain like so:
exports.calculate = function(req, res, next) {
models.Quiz.count().then(function(questions) {
statistics.questions = questions;
return models.Comment.count();
}).then(function(comments) {
statistics.comments = comments;
statistics.average_comments = (statistics.comments / statistics.questions).toFixed(2);
return models.Quiz.findAll({
include: [{
model: models.Comment
}]
});
}).then(function(quizes) {
for (index in quizes) {
if (quizes[index].Comment.length) {
statistics.commented_questions++;
} else {
statistics.no_commented++;
}
}
}).catch(next).finally(next);
};
If you are using a version of Node/IO that has support for the native Promise object you can leverage that a bit to issue concurrent requests since none of them are dependent on each other. Note: the Promise API does not have a finally() method but we can use the second argument for then() to pass an error along.
exports.calculate = function(req, res, next) {
Promise.all([
models.Quiz.count(),
models.Comment.count(),
models.Quiz.findAll({
include: [{
model: models.Comment
}]
})
]).then(function(results)
// `results` is an array of [questions, comments, quizes]
statistics.questions = results[0];
statistics.comments = results[1];
statistics.average_comments = (statistics.comments / statistics.questions).toFixed(2);
for (index in results[2]) {
if (results[2][index].Comment.length) {
statistics.commented_questions++;
} else {
statistics.no_commented++;
}
}
}).then(next, next);
};
Related
I always get the error when I run the code block below -
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
module.exports = (app, spotifyAPI) => {
app.get('/api/search', requireLogin, async (req, res) => {
const URI_BASE = keys.ComputerVisionEndpoint + 'vision/v3.0/analyze';
const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/3/3c/Shaki_waterfall.jpg"; // will be sent as req body
var results;
// making API call to microsoft cognitive services API
try {
results = await axios({
method: 'post',
url: URI_BASE,
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key' : keys.ComputerVision
},
params: {
'visualFeatures': 'Tags',
'details': '',
'language': 'en'
},
data: {
"url": imageUrl,
}
});
} catch (err) {
return res.status(400).send(err);
}
// remove the common ones - indoor, outdoor, ground, wall, person, woman, man, ceiling, floor
const to_filter = results['data']['tags'];
_.remove(to_filter, (item) => {
return (item.name === 'indoor' || item.name === 'outdoor' || item.name === 'ground' || item.name === 'wall'
|| item.name === 'person' || item.name === 'woman' || item.name === 'man' || item.name === 'ceiling'
|| item.name === 'floor'
);
});
// searching for relevant songs and adding them to the playlist
var id;
try {
id = await search_and_add(req, res, spotifyAPI, to_filter, playlist_id);
} catch (err) {
if (err['statusCode'] === 401) {
req.logout();
return res.redirect('/');
}
else {
return res.status(400).send(err);
}
}
});
}
search_and_add = async (req, res, spotifyAPI, to_filter, playlist_id) => {
_.map(to_filter, async (tag) => {
try {
const song_details = await spotifyAPI.searchTracks(tag.name, { limit: 1 });
//const song_uri = song_details['body']['tracks']['items'][0]['id'];
console.log(song_details);
} catch (err) {
throw err;
}
});
return;
// figure out where to re direct user
};
I'm pretty sure it is because of the map statement in the search_and_add function, but I do not know how to get rid of it and provide the same functionality to make the try-catch block work? Could someone help?
You're not doing anything with the promises created by the callback to the _.map(…) call in search_and_add. They just get ignored, are not awaited, and will cause the warning when getting rejected. Presumably you meant to use Promise.all there?
function search_and_add(req, res, spotifyAPI, to_filter, playlist_id) {
return Promise.all(to_filter.map(async (tag) => {
// ^^^^^^^^^^^^
const song_details = await spotifyAPI.searchTracks(tag.name, { limit: 1 });
//const song_uri = song_details['body']['tracks']['items'][0]['id'];
console.log(song_details);
});
}
You are using map function since you need to wrap with Promise.allSettled function.
Promise.allSettled is available in node js 12 version and above.
if you are using less than node 12 then you need to use Promise.all
Promise.allSettled():
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
It is typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, or you'd always like to know the result of each promise.
Promise.all():
The Promise.all() method takes an iterable of promises as an input, and returns a single Promise as an output. This returned promise will resolve when all of the input's promises have resolved and non-promises have returned, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.
It is typically used when there are multiple asynchronous tasks that are dependent on one another to complete successfully, as it does not wait and will reject immediately upon any of the input promises rejecting.
refer this:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
https://sung.codes/blog/2019/05/18/promise-race-vs-promise-any-and-promise-all-vs-promise-allsettled/
const search_and_add = (req, res, spotifyAPI, to_filter, playlist_id) {
return Promise.allSettled(to_filter.map(async (tag) => {
const song_details = await spotifyAPI.searchTracks(tag.name, { limit: 1 });
//const song_uri = song_details['body']['tracks']['items'][0]['id'];
console.log(song_details);
return song_details;
}).catch(function(err){
console.log(err);
return err;
});
}
For error handling in async await:
Async Await is essentially syntactic sugar for promises, and if an await statement errors it will return a rejected promise, Instead of adding try -catch every where we can write a helper function that wraps our express routes to handle all rejected promises of route.
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
then wrap your route function like this
router.get('/users/:id', asyncMiddleware(async (req, res, next) => {
/*
if there is an error thrown in getUserFromDb, asyncMiddleware
will pass it to next() and express will handle the error;
*/
const user = await getUserFromDb({ id: req.params.id })
res.json(user);
}));
Note: You can also use npm package async-middleware for this.
I have the following code that uses callbacks inside promises:
const clue = 'someValue';
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(null, array); // Callback with 'Success'
}
else{
callback(`The array does not includes: ${clue}`); // Callback with Error
}
})
.catch((err) => {
// handle error
callback(`Some error inside the promise chain: ${err}`) // Callback with Error
})
}
and call it like this:
myFunction (someParam, (error, response) => {
if(error) {
console.log(error);
}
else {
// do something with the 'response'
}
})
Reading some documentation, I found that there is some improved way to do this:
const myFunction = (someParam, callback) => {
someAsyncOperation(someParam) // this function returns an array
.then((array) => {
if (array.includes(clue)){
callback(array);
}
else{
callback(`The array does not includes: ${clue}`);
}
}, (e) => {
callback(`Some error happened inside the promise chain: ${e}`);
})
.catch((err) => {
// handle error
callback(`Some error happened with callbacks: ${err}`)
})
}
My question:
In the sense of performance or best practices, it's okay to call the 'callback' function inside the promise as the two ways show or I'm doing something wrong, I mean some promise anti-pattern way ?
This seems really backwards and takes away from the benefits of promises managing errors and passing them down the chain
Return the asynchronous promise from the function and don't interrupt it with callbacks. Then add a catch at the end of the chain
const myFunction = (someParam) => {
// return the promise
return someAsyncOperation(someParam) // this function returns an array
.then((array) => {
return array.includes(clue) ? array : [];
});
}
myFunction(someParam).then(res=>{
if(res.length){
// do something with array
}else{
// no results
}
}).catch(err=>console.log('Something went wrong in chain above this'))
Do not use callbacks from inside of promises, that is an anti-pattern. Once you already have promises, just use them. Don't "unpromisify" to turn them into callbacks - that's moving backwards in code structure. Instead, just return the promise and you can then use .then() handlers to set what you want the resolved value to be or throw an error to set what you want the rejected reason to be:
const clue = 'someValue';
const myFunction = (someParam) => {
return someAsyncOperation(someParam).then(array => {
if (!array.includes(clue)){
// reject promise
throw new Error(`The array does not include: ${clue}`);
}
return array;
});
}
Then, the caller would just do this:
myFunction(someData).then(array => {
// success
console.log(array);
}).catch(err => {
// handle error here which could be either your custom error
// or an error from someAsyncOperation()
console.log(err);
});
This gives you the advantage that the caller can use all the power of promises to synchronize this async operation with any others, to easily propagate errors all to one error handler, to use await with it, etc...
I’m new to node and mongo after 15 years of VB6 and MySql. I’m sure this is not what my final program will use but I need to get a basic understanding of how to call a function in another module and get results back.
I want a module to have a function to open a DB, find in a collection and return the results. I may want to add a couple more functions in that module for other collections too. For now I need it as simple as possible, I can add error handlers, etc later. I been on this for days trying different methods, module.exports={… around the function and with out it, .send, return all with no luck. I understand it’s async so the program may have passed the display point before the data is there.
Here’s what I’ve tried with Mongo running a database of db1 with a collection of col1.
Db1.js
var MongoClient = require('mongodb').MongoClient;
module.exports = {
FindinCol1 : function funk1(req, res) {
MongoClient.connect("mongodb://localhost:27017/db1", function (err,db) {
if (err) {
return console.dir(err);
}
var collection = db.collection('col1');
collection.find().toArray(function (err, items) {
console.log(items);
// res.send(items);
}
);
});
}
};
app.js
a=require('./db1');
b=a.FindinCol1();
console.log(b);
Console.log(items) works when the 'FindinCol1' calls but not console.log(b)(returns 'undefined') so I'm not getting the return or I'm pasted it by the time is returns. I’ve read dozens of post and watched dozens of videos but I'm still stuck at this point. Any help would be greatly appreciated.
As mentioned in another answer, this code is asynchronous, you can't simply return the value you want down the chain of callbacks (nested functions). You need to expose some interface that lets you signal the calling code once you have the value desired (hence, calling them back, or callback).
There is a callback example provided in another answer, but there is an alternative option definitely worth exploring: promises.
Instead of a callback function you call with the desired results, the module returns a promise that can enter two states, fulfilled or rejected. The calling code waits for the promise to enter one of these two states, the appropriate function being called when it does. The module triggers the state change by resolveing or rejecting. Anyways, here is an example using promises:
Db1.js:
// db1.js
var MongoClient = require('mongodb').MongoClient;
/*
node.js has native support for promises in recent versions.
If you are using an older version there are several libraries available:
bluebird, rsvp, Q. I'll use rsvp here as I'm familiar with it.
*/
var Promise = require('rsvp').Promise;
module.exports = {
FindinCol1: function() {
return new Promise(function(resolve, reject) {
MongoClient.connect('mongodb://localhost:27017/db1', function(err, db) {
if (err) {
reject(err);
} else {
resolve(db);
}
}
}).then(function(db) {
return new Promise(function(resolve, reject) {
var collection = db.collection('col1');
collection.find().toArray(function(err, items) {
if (err) {
reject(err);
} else {
console.log(items);
resolve(items);
}
});
});
});
}
};
// app.js
var db = require('./db1');
db.FindinCol1().then(function(items) {
console.info('The promise was fulfilled with items!', items);
}, function(err) {
console.error('The promise was rejected', err, err.stack);
});
Now, more up to date versions of the node.js mongodb driver have native support for promises, you don't have to do any work to wrap callbacks in promises like above. This is a much better example if you are using an up to date driver:
// db1.js
var MongoClient = require('mongodb').MongoClient;
module.exports = {
FindinCol1: function() {
return MongoClient.connect('mongodb://localhost:27017/db1').then(function(db) {
var collection = db.collection('col1');
return collection.find().toArray();
}).then(function(items) {
console.log(items);
return items;
});
}
};
// app.js
var db = require('./db1');
db.FindinCol1().then(function(items) {
console.info('The promise was fulfilled with items!', items);
}, function(err) {
console.error('The promise was rejected', err, err.stack);
});
Promises provide an excellent method for asynchronous control flow, I highly recommend spending some time familiarizing yourself with them.
Yes, this is an async code and with a return you will get the MongoClient object or nothing, based on where you put.
You should use a callback parameter:
module.exports = {
FindinCol1 : function funk1(callback) {
MongoClient.connect("mongodb://localhost:27017/db1", function (err,db) {
if (err) {
return console.dir(err);
}
var collection = db.collection('col1');
collection.find().toArray(function (err, items) {
console.log(items);
return callback(items);
});
});
}
};
Pass a callback function to FindinCol1:
a.FindinCol1(function(items) {
console.log(items);
});
I suggest you to check this article:
https://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks
I’m new to node and mongo after 15 years of VB6 and MySql. I’m sure this is not what my final program will use but I need to get a basic understanding of how to call a function in another module and get results back.
I want a module to have a function to open a DB, find in a collection and return the results. I may want to add a couple more functions in that module for other collections too. For now I need it as simple as possible, I can add error handlers, etc later. I been on this for days trying different methods, module.exports={… around the function and with out it, .send, return all with no luck. I understand it’s async so the program may have passed the display point before the data is there.
Here’s what I’ve tried with Mongo running a database of db1 with a collection of col1.
Db1.js
var MongoClient = require('mongodb').MongoClient;
module.exports = {
FindinCol1 : function funk1(req, res) {
MongoClient.connect("mongodb://localhost:27017/db1", function (err,db) {
if (err) {
return console.dir(err);
}
var collection = db.collection('col1');
collection.find().toArray(function (err, items) {
console.log(items);
// res.send(items);
}
);
});
}
};
app.js
a=require('./db1');
b=a.FindinCol1();
console.log(b);
Console.log(items) works when the 'FindinCol1' calls but not console.log(b)(returns 'undefined') so I'm not getting the return or I'm pasted it by the time is returns. I’ve read dozens of post and watched dozens of videos but I'm still stuck at this point. Any help would be greatly appreciated.
As mentioned in another answer, this code is asynchronous, you can't simply return the value you want down the chain of callbacks (nested functions). You need to expose some interface that lets you signal the calling code once you have the value desired (hence, calling them back, or callback).
There is a callback example provided in another answer, but there is an alternative option definitely worth exploring: promises.
Instead of a callback function you call with the desired results, the module returns a promise that can enter two states, fulfilled or rejected. The calling code waits for the promise to enter one of these two states, the appropriate function being called when it does. The module triggers the state change by resolveing or rejecting. Anyways, here is an example using promises:
Db1.js:
// db1.js
var MongoClient = require('mongodb').MongoClient;
/*
node.js has native support for promises in recent versions.
If you are using an older version there are several libraries available:
bluebird, rsvp, Q. I'll use rsvp here as I'm familiar with it.
*/
var Promise = require('rsvp').Promise;
module.exports = {
FindinCol1: function() {
return new Promise(function(resolve, reject) {
MongoClient.connect('mongodb://localhost:27017/db1', function(err, db) {
if (err) {
reject(err);
} else {
resolve(db);
}
}
}).then(function(db) {
return new Promise(function(resolve, reject) {
var collection = db.collection('col1');
collection.find().toArray(function(err, items) {
if (err) {
reject(err);
} else {
console.log(items);
resolve(items);
}
});
});
});
}
};
// app.js
var db = require('./db1');
db.FindinCol1().then(function(items) {
console.info('The promise was fulfilled with items!', items);
}, function(err) {
console.error('The promise was rejected', err, err.stack);
});
Now, more up to date versions of the node.js mongodb driver have native support for promises, you don't have to do any work to wrap callbacks in promises like above. This is a much better example if you are using an up to date driver:
// db1.js
var MongoClient = require('mongodb').MongoClient;
module.exports = {
FindinCol1: function() {
return MongoClient.connect('mongodb://localhost:27017/db1').then(function(db) {
var collection = db.collection('col1');
return collection.find().toArray();
}).then(function(items) {
console.log(items);
return items;
});
}
};
// app.js
var db = require('./db1');
db.FindinCol1().then(function(items) {
console.info('The promise was fulfilled with items!', items);
}, function(err) {
console.error('The promise was rejected', err, err.stack);
});
Promises provide an excellent method for asynchronous control flow, I highly recommend spending some time familiarizing yourself with them.
Yes, this is an async code and with a return you will get the MongoClient object or nothing, based on where you put.
You should use a callback parameter:
module.exports = {
FindinCol1 : function funk1(callback) {
MongoClient.connect("mongodb://localhost:27017/db1", function (err,db) {
if (err) {
return console.dir(err);
}
var collection = db.collection('col1');
collection.find().toArray(function (err, items) {
console.log(items);
return callback(items);
});
});
}
};
Pass a callback function to FindinCol1:
a.FindinCol1(function(items) {
console.log(items);
});
I suggest you to check this article:
https://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks
I don't understand below part
var q = require("q"),
BlogPost = require("../models/blogPost");
module.exports = {
getAllPosts: getAllPosts
};
function getAllPosts() {
var deferred = q.defer();
BlogPost
.find({})
.sort("-date")
.exec(function(error, posts) {
if (error) {
deferred.reject(error);
} else {
deferred.resolve(posts);
}
});
return deferred.promise;
}
I found above code in controller, but can't understand it. Why at the end we use return deferred.promise? How would I use the getAllPosts later? Can't we just return the posts objects?
You would consume a function that returns a promise like so:
var getAllPosts = require('./your-module-name.js').getAllPosts;
getAllPosts()
.then(function(posts) {
//successfully process the posts
})
.catch(function(err) {
//handle the error
})
.finally(function(){
//clean up anything you need to...
})
Promise is the just representation of the asynchronous result.
It has three states:
1-> success
-> Error
-> Pending
deferred is a just object of promise and it returns after processing one of above state.
We can use javascript code without promise but sometimes we have to give it to make our code execute as asynchronous way.
That's why we use promise