i am taking my first steps with node.js and i came across this issue with passing variable in asynchronous way. I have this piece of code im using to create Facebook user:
req.tmpPassport = {};
var fb = new fbgraph.Facebook(accessToken, 'v2.2');
function initUser() {
fb.me(function (err, me) {
req.tmpPassport.me = me;
console.log(req.tmpPassport.me) // works
});
}
console.log(req.tmpPassport.me) // not working -> undefined var
i tried to figure out why the second log isn't working and i ended up reading about synchronous and asynchronous functions, so in attempt to implement what i read i tried coming up with a solution using callbacks, but no success.
my last attempt was this:
req.tmpPassport = {};
var fb = new fbgraph.Facebook(accessToken, 'v2.2');
function initUser() {
fb.me(function (err, me) {
req.tmpPassport.me = me;
});
fb.my.events(function (err, events) {
//console.log(events);
req.tmpPassport.events = events;
});
fb.my.friends(function (err, result) {
req.tmpPassport.results = result;
});
}
function passUser(){
console.log(req.tmpPassport);
return req.tmpPassport;
}
cp.exec(initUser, passUser);
but its not working...
what i am actually trying to achieve its to render this object with my express router var which looks like this:
router.get('/welcome', securePages, function(req, res, next){
res.render('welcome', {title:'Welcome to aDating', user:req.tmpPassport});
})
but i cant figure out how to pass this object only after created...any help please?
A method of chaining function calls when certain async tasks are done is one way to deal with this.
Looking at the first snippet of code, it would be rewritten as follows:
req.tmpPassport = {};
var fb = new fbgraph.Facebook(accessToken, 'v2.2');
function initUser() {
fb.me(function (err, me) {
console.log(req.tmpPassport.me) // works
req.tmpPassport.me = me;
// triggers execution of the next step
post_populating_passport();
});
}
function post_populating_passport() {
// this function is only executed after the callback from the async call
console.log(req.tmpPassport.me);
}
Node.js is an asynchronous programming language, a fundamental concept in its core. You either need to use function callbacks (not a good practice) or available npms utilities for async flow.
Related
I have a node.js app using express 4 and this is my controller:
var service = require('./category.service');
module.exports = {
findAll: (request, response) => {
service.findAll().then((categories) => {
response.status(200).send(categories);
}, (error) => {
response.status(error.statusCode || 500).json(error);
});
}
};
It calls my service which returns a promise. Everything works but I am having trouble when trying to unit test it.
Basically, I would like to make sure that based on what my service returns, I flush the response with the right status code and body.
So with mocha and sinon it looks something like:
it('Should call service to find all the categories', (done) => {
// Arrange
var expectedCategories = ['foo', 'bar'];
var findAllStub = sandbox.stub(service, 'findAll');
findAllStub.resolves(expectedCategories);
var response = {
status: () => { return response; },
send: () => {}
};
sandbox.spy(response, 'status');
sandbox.spy(response, 'send');
// Act
controller.findAll({}, response);
// Assert
expect(findAllStub.called).to.be.ok;
expect(findAllStub.callCount).to.equal(1);
expect(response.status).to.be.calledWith(200); // not working
expect(response.send).to.be.called; // not working
done();
});
I have tested my similar scenarios when the function I am testing returns itself a promise since I can hook my assertions in the then.
I also have tried to wrap controller.findAll with a Promise and resolve it from the response.send but it didn't work neither.
You should move your assert section into the res.send method to make sure all async tasks are done before the assertions:
var response = {
status: () => { return response; },
send: () => {
try {
// Assert
expect(findAllStub.called).to.be.ok;
expect(findAllStub.callCount).to.equal(1);
expect(response.status).to.be.calledWith(200); // not working
// expect(response.send).to.be.called; // not needed anymore
done();
} catch (err) {
done(err);
}
},
};
The idea here is to have the promise which service.findAll() returns accessible inside the test's code without calling the service. As far as I can see sinon-as-promised which you probably use does not allow to do so. So I just used a native Promise (hope your node version is not too old for it).
const aPromise = Promise.resolve(expectedCategories);
var findAllStub = sandbox.stub(service, 'findAll');
findAllStub.returns(aPromise);
// response = { .... }
controller.findAll({}, response);
aPromise.then(() => {
expect(response.status).to.be.calledWith(200);
expect(response.send).to.be.called;
});
When code is difficult to test it can indicate that there could be different design possibilities to explore, which promote easy testing. What jumps out is that service is enclosed in your module, and the dependency is not exposed at all. I feel like the goal shouldn't be to find a way to test your code AS IS but to find an optimal design.
IMO The goal is to find a way to expose service so that your test can provide a stubbed implementation, so that the logic of findAll can be tested in isolation, synchronously.
One way to do this is to use a library like mockery or rewire. Both are fairly easy to use, (in my experience mockery starts to degrade and become very difficult to maintain as your test suite and number of modules grow) They would allow you to patch the var service = require('./category.service'); by providing your own service object with its own findAll defined.
Another way is to rearchitect your code to expose the service to the caller, in some way. This would allow your caller (the unit test) to provide its own service stub.
One easy way to do this would be to export a function contstructor instead of an object.
module.exports = (userService) => {
// default to the required service
this.service = userService || service;
this.findAll = (request, response) => {
this.service.findAll().then((categories) => {
response.status(200).send(categories);
}, (error) => {
response.status(error.statusCode || 500).json(error);
});
}
};
var ServiceConstructor = require('yourmodule');
var service = new ServiceConstructor();
Now the test can create a stub for service and provide it to the ServiceConstructor to exercise the findAll method. Removing the need for an asynchronous test altogether.
app.get('/ratings', function (req, res){
db.database.find({rating:5}).count(function(err, doc) {
review.FiveStarCount=doc;
});
db.database.find({rating:4}).count(function(err, doc) {
review.FourStarCount=doc;
});
I am new to using the MEAN stack and trying to return the values of the number of 5 and 4 star reviews. I can retrieve the number of 5 stars just fine, but when I do multiple , I seem to be running into trouble and not sure how to set it up.
You kind of need to get the gist of how to handle control flow while doing async operations.
The function execution would not stop, unlike php, when you are running an async operation.
To accomplish what you are trying to do, the second db call needs to be inside the callback of the first. Try this out.
app.get('/ratings', function (req, res, next) {
db.database.find({rating:5}).count(function(err, doc) {
// always handle error first
if (err) {
return next(err);
}
if (doc) {
review.FiveStarCount = doc;
}
db.database.find({rating:4}).count(function(err, doc) {
if (err) {
return next(err);
}
if (doc) {
review.FourStarCount = doc;
}
console.log(review.FiveStarCount, review.FourStarCount);
});
});
});
So it seems like you are running against the async nature of JavaScript. in the case of the functions above, they will not execute in the order you have written (and you wouldn't want them too!) because they are async. There are few simple solutions to get you going while you learn more about the problem.
Solution 1: Nested callbacks. Hard to read. also, it waits for the first to complete before firing the second, possibly slowing everything down.
app.get('/ratings', function(req, res) {
var review = {};
db.database.find({rating:5}).count(function(err, doc) {
review.FiveStarCount = doc;
db.database.find({rating:4}).count(function(err, doc) {
review.FourStarCount = doc;
// DO something with data
res.json(review);
});
});
Solution 2: Latch. This has the advantage of making the requests in parallel and not being as awful to read.
app.get('/ratings', function(req, res) {
var review = {};
db.database.find({rating:5}).count(function(err, doc) {
review.FiveStarCount=doc;
if (review.FiveStarCount && review.FourStarCount) {
// DO something here
// Will only execute if the other has finished
}
});
db.database.find({rating:4}).count(function(err, doc) {
review.FourStarCount=doc;
if (review.FiveStarCount && review.FourStarCount) {
// DO something here
// Will only execute if the other has finished
}
});
});
There are course more solutions, such as maybe a better query or promises. Maybe read a bit more about async JavaScript here to get a better understanding.
var Q = require('q');
Q.nfcall(client.get("time_clock", function (err, reply) {
var time = reply.toString();
return time;
})).then(function(time) {
client.get("time_predicted", function (err, replier) {
mom=replier.toString();
res.render('time', {watch: time, moment: mom});
})
}).fail(function(err){
console.log('Error.')})
.done();
};
This code fails. The code below works, without using promises (shown below). The code I want to fix is above.
client.get("time_clock", function (err, reply) {
time=reply.toString();
console.log("in here"+time); // Will print `OK`
client.get("time_predicted", function (err, replier) {
mom=replier.toString();
res.render('time', {watch: time, moment: mom});
});
});
What do I need to change in the first code example to make it work? (Note: it would be even better if I could call res.render at the very end, in the last or another 'then').
I won't directly answer to your question; but a simple solution is to use redis-then, a redis library for NodeJS that uses promises.
I am able to insert and retrieve data from an neDB database in nodejs. But I cannot pass the data outside of the function that retrieves it.
I have read through the neDB documentation and I searched for and tried different combinations of callbacks and returns (see code below) without finding a solution.
I'm new to javascript so I do not know if I am misunderstanding how to use variables in general or if this issue is related to using neDB specifically or both.
Could someone please explain why "x" in my code does not ever contain the docs JSON results from the database? How can I make it work?
var fs = require('fs'),
Datastore = require('nedb')
, db = new Datastore({ filename: 'datastore', autoload: true });
//generate data to add to datafile
var document = { Shift: "Late"
, StartTime: "4:00PM"
, EndTime: "12:00AM"
};
// add the generated data to datafile
db.insert(document, function (err, newDoc) {
});
//test to ensure that this search returns data
db.find({ }, function (err, docs) {
console.log(JSON.stringify(docs)); // logs all of the data in docs
});
//attempt to get a variable "x" that has all
//of the data from the datafile
var x = function(err, callback){
db.find({ }, function (err, docs) {
callback(docs);
});
};
console.log(x); //logs "[Function]"
var x = db.find({ }, function (err, docs) {
return docs;
});
console.log(x); //logs "undefined"
var x = db.find({ }, function (err, docs) {
});
console.log(x); //logs "undefined"*
Callbacks are generally asynchronous in JavaScript meaning you can not use assignment operator, consequently you do not return anything from the callback function.
When you call an async function execution of you programme carries on, passing the 'var x = whatever' statement. The assignment to a variable, the result of whatever callback is received, you need to perform from within the callback itself... what you need is something in the lines of ...
var x = null;
db.find({ }, function (err, docs) {
x = docs;
do_something_when_you_get_your_result();
});
function do_something_when_you_get_your_result() {
console.log(x); // x have docs now
}
EDIT
Here is a nice blog post about asynchronous programming. And there is a lot more resources on this topic that you can pick up.
This is a popular library to help with async flow-control for node.
P.S.
Hope this helps. Please, by all means ask if you need something clarified :)
I ran into the same problem. In the end I used a combination between async-await and a promise with resolve to solve it.
In your example the following would work:
var x = new Promise((resolve,reject) {
db.find({ }, function (err, docs) {
resolve(docs);
});
});
console.log(x);
I had to learn a bit about async functions to get it right. For those who are looking for specific help about getting a return value from nedb, here's a snippet of what worked for me. I was using it in electron.
function findUser(searchParams,callBackFn)
{
db.find({}, function (err, docs))
{
//executes the callback
callBackFn(docs)
};
}
usage
findUser('John Doe',/*this is the callback -> */function(users){
for(i = 0; i < users.length; i++){
//the data will be here now
//eg users.phone will display the user's phone number
}})
I'm using the Node.JS driver for MongoDB, and I'd like to perform a synchronous query, like such:
function getAThing()
{
var db = new mongo.Db("mydatabase", server, {});
db.open(function(err, db)
{
db.authenticate("myuser", "mypassword", function(err, success)
{
if (success)
{
db.collection("Things", function(err, collection)
{
collection.findOne({ name : "bob"}, function(err, thing)
{
return thing;
});
});
}
});
});
}
The problem is, db.open is an asychronous call (it doesn't block), so the getAThing returns "undefined" and I want it to return the results of the query. I'm sure I could some sort of blocking mechanism, but I'd like to know the right way to do something like this.
ES 6 (Node 8+)
You can utilize async/await
await operator pauses the execution of asynchronous function until the Promise is resolved and returns the value.
This way your code will work in synchronous way:
const query = MySchema.findOne({ name: /tester/gi });
const userData = await query.exec();
console.log(userData)
Older Solution - June 2013 ;)
Now the Mongo Sync is available, this is the right way to make a synchronous MongoDB query in Node.js.
I am using this for the same. You can just write sync method like below:
var Server = require("mongo-sync").Server;
var server = new Server('127.0.0.1');
var result = server.db("testdb").getCollection("testCollection").find().toArray();
console.log(result);
Note: Its dependent on the node-fiber and some issues are there with it on windows 8.
Happy coding :)
There's no way to make this synchronous w/o some sort of terrible hack. The right way is to have getAThing accept a callback function as a parameter and then call that function once thing is available.
function getAThing(callback)
{
var db = new mongo.Db("mydatabase", server, {});
db.open(function(err, db)
{
db.authenticate("myuser", "mypassword", function(err, success)
{
if (success)
{
db.collection("Things", function(err, collection)
{
collection.findOne({ name : "bob"}, function(err, thing)
{
db.close();
callback(err, thing);
});
});
}
});
});
}
Node 7.6+ Update
async/await now provides a way of coding in a synchronous style when using asynchronous APIs that return promises (like the native MongoDB driver does).
Using this approach, the above method can be written as:
async function getAThing() {
let db = await mongodb.MongoClient.connect('mongodb://server/mydatabase');
if (await db.authenticate("myuser", "mypassword")) {
let thing = await db.collection("Things").findOne({ name: "bob" });
await db.close();
return thing;
}
}
Which you can then call from another async function as let thing = await getAThing();.
However, it's worth noting that MongoClient provides a connection pool, so you shouldn't be opening and closing it within this method. Instead, call MongoClient.connect during your app startup and then simplify your method to:
async function getAThing() {
return db.collection("Things").findOne({ name: "bob" });
}
Note that we don't call await within the method, instead directly returning the promise that's returned by findOne.
While it's not strictly synchronous, a pattern I've repeatedly adopted and found very useful is to use co and promisify yield on asynchronous functions. For mongo, you could rewrite the above:
var query = co( function* () {
var db = new mongo.Db("mydatabase", server, {});
db = promisify.object( db );
db = yield db.open();
yield db.authenticate("myuser", "mypassword");
var collection = yield db.collection("Things");
return yield collection.findOne( { name : "bob"} );
});
query.then( result => {
} ).catch( err => {
} );
This means:
You can write "synchronous"-like code with any asynchronous library
Errors are thrown from the callbacks, meaning you don't need the success check
You can pass the result as a promise to any other piece of code