Delaying events until a callback is called (Node.js) - node.js

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.

Related

Does order matter in REST api express, ie. get -> post -> put -> delete?

Does it matter if you implement the get method before another method such as post for example implement app.post() before app.get()? I am not sure why there would be significance in changing the order, but in the express app that I built if I implemented post before get, my data would buffer and then be posted every other call, the posting was inconsistent. When I switched the order the issue was fixed.
This is the code for the requests
const xhrPost = new XMLHttpRequest();
const xhrGet = new XMLHttpRequest();
//sends data to DB
xhrPost.open("POST", '/endgame', true);
xhrPost.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhrPost.send(JSON.stringify({
playerScore: score
}));
//when data is done being posted, get list of scores from db
xhrPost.onreadystatechange = function() {
console.log(this.responseText);
if (this.readyState === 4 && this.status === 200) {
xhrGet.open("GET", '/endgame', true);
xhrGet.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhrGet.send();
}
}
//when scores retrieved display results on console
xhrGet.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
console.table(JSON.parse(this.responseText));
var data = (JSON.parse(this.responseText));
ctx.fillText(data[0].playerScore, 50, 150);
}
};
and this is the server side code
mongodb.MongoClient.connect(url, (error, database) => {
if (error) return process.exit(1)
const db = database.db('js-snake-scores')
app.post('/endgame', (req, res) => {
let score = req.body
db.collection('scores')
.insert(score, (error, results) => {
if (error) return
res.send(results)
})
})
app.get('/endgame', (req, res) => {
db.collection('scores')
.find({}, {
playerScore: 1
}).toArray((err, data) => {
if (err) return next(err)
res.send(data)
})
})
app.use(express.static(path.join(__dirname, 'static')))
app.listen(process.env.PORT || 5000)
})
Does it matter if you implement the get method before another method such as post for example implement app.post() before app.get()?
No. Order matters only when two routes would handle both the same path and the same method. So, since app.post() and app.get() each only intercept different methods, they don't compete in any way and thus their relative ordering to each other does not matter. Only one will ever trigger on a GET and only the other one will ever trigger on a POST regardless of their order of definition.
If you saw a difference in behavior due to the order, then it must have been due to some other effect besides just an app.get() and an app.post() with the same path because those two are not ever activated on the same request. If we could see the two implementations of code where you say order mattered when you switched them, then we could likely offer you a better idea of why you saw a difference in behavior. app.post() and app.get() ordering by themselves would not cause what you described.

Error: Can't set headers after they are sent Braintree

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.

Wait for an event to happen before sending HTTP response in NodeJS?

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

Wait a async operation before execute render in express using NodeJs

I'm starting to use NodeJs recently and I'm trying to create a API that will get some information from web compile it and show to the user.
My question is the follow
router.get('/', function (req, res, next) {
https.get(pageUrl, function (res) {
res.on('data', function (responseBuffer) {
//Important info;
info = responseBuffer;
}
}
res.render('page', { important: info});
}
How can I wait until I have the "info" var and then send the res.render. Because right now if I try to wait it usually the program ends and don't wait the result.
Thanks.
Assuming your https.get call gives you a stream with an 'end' event [1], you can do the following:
router.get('/', function (req, res, next) {
https.get(pageUrl, function (res) {
var info;
res.on('data', function (responseBuffer) {
//Important info;
info = responseBuffer;
}
res.on('end', function() {
res.render('page', { important: info});
})
}
}
Note that the above code will not work because you shadowed the base res parameter with the res parameter from the https.get callback.
Also, note that the 'data' event may be emitted several times (again, assuming a standard stream implementation[1]), so you should accumulate the results inside your info variable.
[1] Could you please post more information about your code, such as where the https library comes from (is it the standard HTTPS lib?).
Personal thought: I highly suggest using the request module, disponible on NPM via npm install request, for HTTP(S) requests to external services. It's got a neat interface, is simple to use and handles a lot of situations for you (redirects are one example, JSON and Content-Type another).

Waiting for an event in Node.js

I have been playing with Node.js for some time.
I have the following piece of script in my server:
socket.on('auth', function(uid, key) {
client.hgetall(uid, function (err, data) {
console.log(data);
if(key != data['key']) {
socket.disconnect();
}
this.user = data;
});
});
socket.on('loginGame', function(gameId) {
checkAuth();
console.log(user);
if(!games[gameId]) {
games[gameId] = {};
}
games[gameId][uid] = uid;
});
In my client;
socket.on('connect', function(){
socket.emit('auth', 1, 1);
socket.emit('loginGame', 1);
});
When I run this code, loginGame function finishes before auth function as I am doing I/O (using redis). I know this a feature of node.js, but I think I am missing something. How can I overcome this issue as auth function needs to finish before running any function. (I have a couple of more functions which need to run in serial)
I have found a node.js module: https://github.com/creationix/step . However, is this the only option? I think this is something most node.js developer might need. I am not that good at JS either.
What is the best and most elegant way of solving this issue?
Thanks,
Have the server send back an acknowledgement when auth is successful, and send loginGame in the handler for that message.

Resources