Chain parameters between async functions with Q in node.js - node.js

How can I chain the parameters that I need for my both async functions.
The first function fs.readFile returns the content of the file in the callback function as second parameter.
The second function marked requires this content as first parameter. The second parameter is optional and can be an options object. The third parameter is the callback that should give me the converted content as second parameter.
Currently I've tried this code:
var readFile = q.nfbind(fs.readFile);
var md = q.nfbind(marked);
readFile(fileName, 'UTF8')
.then(md)
.then(function (html) {
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', html.length);
res.status(200);
res.end(html);
})
.catch(function (error) {
res.setHeader('Content-Type', 'text/plain');
res.send(500, 'server error: ' + error);
res.end();
})
.done();
But it doesn't work, because the marked function needs the second parameter when it was called with an callback function as third parameter. How can I set the second parameter, to call the marked function correctly?

If you simply replace the .then(md) line with .then(marked), then the result of calling fs.readFile (the value with which the promise was fulfilled) will be passed to marked.

Related

Nodejs global variable scope issue

I'm quite new to Nodejs. In the following code I am getting json data from an API.
let data_json = ''; // global variable
app.get('/', (req, res) => {
request('http://my-api.com/data-export.json', (error, response, body) => {
data_json = JSON.parse(body);
console.log( data_json ); // data prints successfully
});
console.log(data_json, 'Data Test - outside request code'); // no data is printed
})
data_json is my global variable and I assign the data returned by the request function. Within that function the json data prints just fine. But I try printing the same data outside the request function and nothing prints out.
What mistake am I making?
Instead of waiting for request to resolve (get data from your API), Node.js will execute the code outside, and it will print nothing because there is still nothing at the moment of execution, and only after node gets data from your api (which will take a few milliseconds) will it execute the code inside the request. This is because nodejs is asynchronous and non-blocking language, meaning it will not block or halt the code until your api returns data, it will just keep going and finish later when it gets the response.
It's a good practice to do all of the data manipulation you want inside the callback function, unfortunately you can't rely on on the structure you have.
Here's an example of your code, just commented out the order of operations:
let data_json = ''; // global variable
app.get('/', (req, res) => {
//NodeJS STARTS executing this code
request('http://my-api.com/data-export.json', (error, response, body) => {
//NodeJS executes this code last, after the data is loaded from the server
data_json = JSON.parse(body);
console.log( data_json );
//You should do all of your data_json manipluation here
//Eg saving stuff to the database, processing data, just usual logic ya know
});
//NodeJS executes this code 2nd, before your server responds with data
//Because it doesn't want to block the entire code until it gets a response
console.log(data_json, 'Data Test - outside request code');
})
So let's say you want to make another request with the data from the first request - you will have to do something like this:
request('https://your-api.com/export-data.json', (err, res, body) => {
request('https://your-api.com/2nd-endpoint.json', (err, res, body) => {
//Process data and repeat
})
})
As you can see, that pattern can become very messy very quickly - this is called a callback hell, so to avoid having a lot of nested requests, there is a syntactic sugar to make this code look far more fancy and maintainable, it's called Async/Await pattern. Here's how it works:
let data_json = ''
app.get('/', async (req,res) => {
try{
let response = await request('https://your-api.com/endpoint')
data_json = response.body
} catch(error) {
//Handle error how you see fit
}
console.log(data_json) //It will work
})
This code does the same thing as the one you have, but the difference is that you can make as many await request(...) as you want one after another, and no nesting.
The only difference is that you have to declare that your function is asynchronous async (req, res) => {...} and that all of the let var = await request(...) need to be nested inside try-catch block. This is so you can catch your errors. You can have all of your requests inside catch block if you think that's necessary.
Hopefully this helped a bit :)
The console.log occurs before your request, check out ways to get asynchronous data: callback, promises or async-await. Nodejs APIs are async(most of them) so outer console.log will be executed before request API call completes.
let data_json = ''; // global variable
app.get('/', (req, res) => {
let pr = new Promise(function(resolve, reject) {
request('http://my-api.com/data-export.json', (error, response, body) => {
if (error) {
reject(error)
} else {
data_json = JSON.parse(body);
console.log(data_json); // data prints successfully
resolve(data_json)
}
});
})
pr.then(function(data) {
// data also will have data_json
// handle response here
console.log(data_json); // data prints successfully
}).catch(function(err) {
// handle error here
})
})
If you don't want to create a promise wrapper, you can use request-promise-native (uses native Promises) created by the Request module team.
Learn callbacks, promises and of course async-await.

In node, how do you wait until a callback has been called?

I have a function which resolves by taking a callback like function(error, result) { ... } as a parameter. I'm trying to use mocha to test this function, but the problem is that the function returns asynchronously, so there's no good place for me to put the done(). If I put inside my result handler, it takes too long and mocha times out. If I put it outside, the test always passes because the handler hasn't been called yet. Here is my code. What's the best way to get around this?
lbl.createLabels is a function that takes an array of customers, and a directory, and creates a bunch of files in that directory, and then asynchronously calls the callback of type: function(error, callback).
describe('Tests', () => {
it('returns a list of customer objects', (done) => {
lbl.createLabels(customers, __dirname + "/..", (err, result) => {
err.should.equal(undefined)
result.should.be.a('array')
result[0].should.have.property('id')
result[0].should.have.property('tracking')
result[0].should.have.property('pdfPath')
const a = {prop:3}
a.prop.should.be.an('array')
done() // putting done() here results in a timeout
})
done() // putting done here results in the test exiting before the callback gets called
})
})
Mocha's documentation has an entire section describing how to test asynchronous code:
https://mochajs.org/#asynchronous-code
Testing asynchronous code with Mocha could not be simpler! Simply invoke the callback when your test is complete. By adding a callback (usually named done) to it(), Mocha will know that it should wait for this function to be called to complete the test.
describe('User', function() {
describe('#save()', function() {
it('should save without error', function(done) {
var user = new User('Luna');
user.save(function(err) {
if (err) done(err);
else done();
});
});
});
});

Why isn't next function in my express code working properly?

So basically I am trying to carry out function synchronously using next but Its not happening synchronously: Route.JS
router.post('/era',function(req,res,next){
console.log("got request");
controlleri.book(req,res,next);
booking_controller.book(req,res);
console.log("done in server.js");
next();
});
controlleri.book and booking_controller.book both have database calls but
Inspite of adding next() in controlleri.book, booking_controller.book starts executing before next being called in the first function.
Tell me if both functions are needed.
Edit:
Tried using async library still its not going synchrounosly Code:
router.post('/era',function(req,res){
console.log("got request");
async.series([
function(callback){
console.log("hi");
controlleri.book(req,res);
callback(null);
},
function(callback){
console.log("hias");
booking_controller.book(req,res);
callback(null);
}
]);
Second function begins before completing First one
Both of your functions take req, res and next as their arguments which is a signature for a middleware function. Further, express middleware is executed in order. Why not just make each of your functions into their own middleware and in the execution order you need them in?
// POST executes controlleri.book
// then booking_controller.book,
// then the log code
router.post('/era', controlleri.book, booking_controller.book, function(req, res, next) {
console.log("done in server.js");
});
This way, next within controlleri.book will be booking_controller.book, and next in booking_controller.book will be the last method that logs "done in server.js"
Use aync library for doinf operations syncronously.
next() is a middleware which tells that the request shouldn't be returned but there are more functions to be applied on the request.The functions it gets to operate are performed asynchronously because that's node's default behaviour.
async.series([
controlleri.book(callback){
// do some stuff ...
callback(null, 'one');
},
booking_controller.book(callback){
// do some more stuff ...
callback(null, 'two');
}
],
// optional callback
function(err, results){
// results is now equal to ['one', 'two']
});
async.series takes an array of functions to be performed in order (synchronously). The callback passed in each function is mandatory.

Can't return response on callback (headers already sent)

This answer tells me to return the response on callback, which is exactly what I'm trying to do: Block function whilst waiting for response
module.exports = {
generator: function(req, res) {
sails.encoder.on('end', function(png_data){
return res.send('hey');
});
sails.encoder.encode('test');
}
}
Returning res.send outside of the callback always work, but when I put it inside of the callback I get:
Can't set headers after they are sent.
What am I missing?

Limiting requests with the async and request modules

I'm combining the async and request modules to make api requests asynchronously and with rate limiting.
Here is my code
var requestApi = function(data){
request(data.url, function (error, response, body) {
console.log(body);
});
};
async.forEachLimit(data, 5, requestApi, function(err){
// do some error handling.
});
Data contains all the urls I make request to. Am limiting the number of concurrent request to 5 using forEachLimit method. This code makes the first 5 request then stops.
In the async docs it says "The iterator is passed a callback which must be called once it has completed". But I don't understand this, what should I be doing to signal that the request has completed?
First, you shall add callback to your iterator function:
var requestApi = function(data, next){
request(data.url, function (error, response, body) {
console.log(body);
next(error);
});
};
next(); or next(null); tells Async that all processing is done. next(error); indicates an error (if error not null).
After processing all requests Async calls its callback function with err == null:
async.forEachLimit(data, 5, requestApi, function(err){
// err contains the first error or null
if (err) throw err;
console.log('All requests processed!');
});
Async calls its callback immediately after receiving the first error or after all requests completed succesfully.

Resources