after some freecodecamp I started doing the Express js tutorial from MDN (https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page 2) for some backend.
I am stuck at understanding where the callback in the async.parallel is coming from and what is represents.
If I delete the callback the site wont load, so it must have some important meaning but unfortunately I have no glue. Is it calling the function(err, results) { res.render(‘index’, […] }) to make the result availalble for data?
var Book = require(’…/models/book’);
var async = require(‘async’);
exports.index = function(req, res) {
async.parallel({
book_count: function(callback) {
Book.countDocuments({}, callback);
},
[...]
[...]
function(err, results) {
res.render('index', {
title: 'Local Library Home',
error: err, data: results
});
});
};
A Callback is a generic function invoked upon the completion of an asynchronous request. In this particular instance, the Callback is being utilized as a method of getting the data out of the asynchronous request to fill the number of books on your page. These are required because these query's are non-blocking, meaning Javascript will keep executing other surrounding code until the Callback is invoked. If you want more detail on how they work in general look here as previously mentioned by #dnp1204. I hope this answered you question.
Related
I try to execute two functions in a post request but only one is triggered. The function makeEntry executes but the function afterwards renderEntries does'nt. Can anyone help pls?
Here is the code: https://codepaste.net/bpsxsy
This is due to how callbacks work in Javascript. Here is the part which matters:
app.post('/guestbook_post', urlencodedparser, function(req, res){
makeEntry(req, res, Guestbook);
renderEntries(res, Guestbook);
});
You pass res to both functions. But makeEntry invokes res.send() which means it will end the request and send the response back to the client. However, renderEntries was not yet executed, but still waiting in event loop. It will be executed the next time you make a post request, which will lead to very confusing and buggy behaviour.
To make it work as intendeed, refactor functions makeEntry and renderEntries to return the needed result object and render it to the client once. Something like this:
app.post('/guestbook_post', urlencodedparser, function(req, res){
makeEntry(req, Guestbook, (err, entry) => {
renderEntries(entry, Guestbook, (err, result) => {
res.send(result);
});
});
});
I am currently working on an admin panel for this website I am creating, so I am able to accept payments via Braintree but I need to implement the ability to retrieve a customers transactions but once a header is sent it sends just one of them and not the whole thing. Is it possible to combine the json to an array so it will send in the one header?
CODE:
router.get('/:cid/test', function(req, res) {
var stream = gateway.transaction.search(function (search) {
search.customerId().is(req.params.cid);
}, function (err, response) {
response.each(function (err, transaction) {
return res.render('admin/test', {transaction: transaction});
});
});
});
This is solely following the Braintree documentation and I know exactly why the error occurs. Any help is really appreciated and I am terrible at explaining so if you need to know more information please give me a holler!
UPDATE: So, I figured I would explore another method and I noticed the 'response' gives back an array of ids. So I will just use EJS to loop through all those and then have a seperate page for each transaction.
Disclaimer: I work for Braintree :)
As Robert noted, you can only call res.render (or any of the response methods that end the request) once per request (hence the error from express).
Unfortunately, you cannot treat response as an array, so you will need to use
one of the two documented ways of interacting with search responses. I personally prefer the streams approach because it is clearer:
app.get('/stream', function (req, res) {
var transactions = []
var transactionStream = gateway.transaction.search(function (search) {
search.customerId().is(req.params.cid);
})
transactionStream.on('data', function (transaction) {
transactions.push(transaction)
})
transactionStream.on('error', function () { /* handle errors */ })
transactionStream.on('end', function () {
res.json({transactions: transactions});
})
})
Alternately, you can use the ids property of response to compare the transactions array that you build from each to know when to end the request:
app.get('/calback', function (req, res) {
var transactionStream = gateway.transaction.search(function (search) {
search.customerId().is(req.params.cid);
}, function (err, response) {
var transactions = []
response.each(function (err, transaction) {
transactions.push(transaction)
if (transactions.length === response.ids.length) {
res.json({transactions: transactions});
}
})
})
})
You can only render one response per route. So you can only call this once and not in a loop:
res.render('admin/test', {transaction: transaction}); });
You can use the each method to iterate through the response and build up a result:
var transactions =[];
response.each(function (err, transaction) { transactions.push(transaction) });
return res.render('admin/test', {transaction: transactions});
That would work if the each method is synchronous. If it's not (and Nick would know), use the solution below.
I'm looking for a solution to waiting for an event to happen before sending a HTTP response.
Use Case
The idea is I call a function in one of my routes: zwave.connect("/dev/ttyACM5"); This function return immediately.
But there exists 2 events that notice about if it succeed or fail to connect the device:
zwave.on('driver ready', function(){...});
zwave.on('driver failed', function(){...});
In my route, I would like to know if the device succeed or fail to connect before sending the HTTP response.
My "solution"
When an event happen, I save the event in a database:
zwave.on('driver ready', function(){
//In the database, save the fact the event happened, here it's event "CONNECTED"
});
In my route, execute the connect function and wait for the event to
appear in the database:
router.get('/', function(request, response, next) {
zwave.connect("/dev/ttyACM5");
waitForEvent("CONNECTED", 5, null, function(){
response.redirect(/connected);
});
});
// The function use to wait for the event
waitForEvent: function(eventType, nbCallMax, nbCall, callback){
if(nbCall == null) nbCall = 1;
if(nbCallMax == null) nbCallMax = 1;
// Looking for event to happen (return true if event happened, false otherwise
event = findEventInDataBase(eventType);
if(event){
waitForEvent(eventType, nbCallMax, nbCall, callback);
}else{
setTimeout(waitForEvent(eventType, callback, nbCallMax, (nbCall+1)), 1500);
}
}
I don't think it is a good practice because it iterates calls over the database.
So what are your opinions/suggestions about it?
I've gone ahead and added the asynchronous and control-flow tags to your question because at the core of it, that is what you're asking about. (As an aside, if you're not using ES6 you should be able to translate the code below back to ES5.)
TL;DR
There are a lot of ways to handle async control flow in JavaScript (see also: What is the best control flow module for node.js?). You are looking for a structured way to handle it—likely Promises or the Reactive Extensions for JavaScript (a.k.a RxJS).
Example using a Promise
From MDN:
The Promise object is used for asynchronous computations. A Promise represents a value which may be available now, or in the future, or never.
The async computation in your case is the computation of a boolean value describing the success or failure to connect to the device. To do so, you can wrap the call to connect in a Promise object like so:
const p = new Promise((resolve) => {
// This assumes that the events are mutually exclusive
zwave.connect('/dev/ttyACM5');
zwave.on('driver ready', () => resolve(true));
zwave.on('driver failed', () => resolve(false));
});
Once you have a Promise representing the state of the connection, you can attach functions to its "future" value:
// Inside your route file
const p = /* ... */;
router.get('/', function(request, response, next) {
p.then(successful => {
if (successful) {
response.redirect('/connected');
}
else {
response.redirect('/failure');
}
});
});
You can learn more about Promises on MDN, or by reading one of many other resources on the topic (e.g. You're Missing the Point of Promises).
Have you tried this? From the look of it, your zwave probably have already implemented an EventEmmiter, you just need to attach a listener to it
router.get('/', function(request, response, next) {
zwave.connect("/dev/ttyACM5");
zwave.once('driver ready', function(){
response.redirect(/connected);
});
});
There is a npm sync module also. which is used for synchronize the process of executing the query.
When you want to run parallel queries in synchronous way then node restrict to do that because it never wait for response. and sync module is much perfect for that kind of solution.
Sample code
/*require sync module*/
var Sync = require('sync');
app.get('/',function(req,res,next){
story.find().exec(function(err,data){
var sync_function_data = find_user.sync(null, {name: "sanjeev"});
res.send({story:data,user:sync_function_data});
});
});
/*****sync function defined here *******/
function find_user(req_json, callback) {
process.nextTick(function () {
users.find(req_json,function (err,data)
{
if (!err) {
callback(null, data);
} else {
callback(null, err);
}
});
});
}
reference link: https://www.npmjs.com/package/sync
In all most all node.js tutorial I can see this pattern of coding.
app.get('/user', function (req, res) {
User.findById(req.body.id, function(err, user) {
if (err) throw err;
res.send(user);
});
});
Now this line res.send(user) is scaring me. User.findById is asynchronous and this api end point may get simultaneous hits and what is the guarantee that we have right res at the time of we reply to the req.
As of now I am creating closure to maintain the variable value.
app.get('/user', function (req, res) {
User.findById(req.body.id, function(res){
return function(err, user) {
if (err) throw err;
res.send(user);
}
}(res));
});
What is the best way to solve this problem?
You do not need to add closure protection for req and res. They are already properly scoped to one callback and are not shared or accessible with code running from other requests.
The app.get() function call has already created a closure for the req and res objects. So, any code within that callback or called from that callback and passed those arguments is guaranteed to get the right objects, no matter how much async coding is going on anywhere.
app.get('/user', function (req, res) {
// This is already a unique closure right here with unique copies of
// the req and res arguments. These arguments cannot be accessed
// by any other request or by any code out of this scope
// unless you copy them yourself to some shared location
// with concurrency access issues
User.findById(req.body.id, function(err, user) {
if (err) throw err;
res.send(user);
});
});
The only other thing that has to happen correctly is for node.js to never reuse those req and res objects for other requests that might happen while this one is being processed asynchronously. And, it DOES guarantee that. req and res are created new for each request.
Furthermore, there isn't even a way from one request to get access to the req or res from another request unless YOU somehow share it inappropriately (like stuffing it in a shared module level variable). So, as long as you only use the req or res directly from the callback in the lexical scope or you pass them to a function you are calling and none of your code actually saves them someplace that has concurrency access issues, then there are no issues.
So, to summarize. node.js itself does not have any concurrency issues with the req and res objects. So, as long as you don't create a problem by storing req or res in some shared location that has concurrency access issues from your own code, this is not a problem.
Wrong Code Example
Just to show you what wrong code would look like that does cause a problem:
var _res;
app.get('/user', function (req, res) {
_res = res;
User.findById(_req.body.id, function(err, user) {
if (err) throw err;
_res.send(user);
});
});
app.get('/account', function (req, res) {
_res = res;
User.findById(req.body.account, function(err, account) {
if (err) throw err;
_res.send(account);
});
});
Here, the code is assigning res to a higher scoped and shared variable and then attempting to use it after an async call. This would be bad and does create a concurrency issue. But, this isn't an issue with node.js per se, but an issue with bad code that isn't aware of proper concurrency design. And, there's no reason to copy a variable being used in an async operation to a higher and shared scope either. If it needs to be elsewhere, it should be passed as an argument as that will keep it unique per request.
Here are some reading references on "Javascript lexical scope".
What You Should Know About Javascript Scope
MDN: Lexical Scope and Closures
You Don't Know JS: Scopes & Closures
Everything You Wanted to Know About Javascript Scope
Your attempt is both incorrect and unnecessary. In this case the lexical scoping takes care of what you think is an issue.
app.get('/user', function (req, res) { // <- the `res` here
User.findById(req.body.id, function(err, user) {
if (err) throw err;
res.send(user); // is the same `res` here
});
});
due to the already existing closure created by definition of the function passed as the callback.
I think your scare is unfounded. When a request comes in each one gets its own reference to the callback function you pass to it. Which in this case is the one that handles the response. This function gets the res object, which is then used in the next callback function passed to the findById due to lexical scoping in JavaSript.
app.get('/user', function (req, res) {
User.findById(req.body.id, function(err, user) {
if (err) throw err;
res.send(user); // res here is unique for every new request,no need to worry :)
});
});
I am using Node.js with Express and have code similar to this as part of my routes:
requireLogin: function(req, res, next) {
User.find(req.session.userId)
.on('success', function(user) {
req.addListener('data', function(chunk) {
console.log("DATA: " + chunk);
}
next()
}
}
I am using Sequelize and the User.find method is accessing the database. The trouble is, the request 'data' event that I bind to is never fired. It seems that the data event had already been triggered and handled by the time the user is returned from the database and it's too late to do anything with it. In the example above I could just move the req.addListener to outside the database callback, but in reality I am calling next() here which can't be moved.
All of the following route middleware that is called by next() then doesn't have access to the request data since these events have already been fired. Worse than that, they just hang waiting for the data event from req because it has already happened.
How can I somehow delay the data event so that it can be bound to from within the database callback? Or have I misunderstood something fundamental and need to change my way of going about this?
Thanks a lot.
Edit: I found a relevant discussion in the nodejs Google group which suggests there isn't a solution that will work for me.
var cache = new function () {
var arr = [],
cbs = [];
this.add = function(data) {
arr.push(data);
cbs.forEach(function(cb) {
cb(arr);
});
}
this.get = function(cb) {
cbs.push(arr);
if (arr.length > 0) {
cb(arr);
}
}
};
req.addListener('data', function(chunk) {
cache.add(chunk);
};
User.find(
req.session.userId
).on('success', function(user) {
cache.get(function(data) {
// stuff
next();
});
};
I presume what you actually want is some kind of message caching. Now this is a vague proof of concept. What you actually want depends on your code.
If you have any kind of deferred library / abstraction available then the code will become a lot smaller.