Nodejs promises, launches function before previous one finished - node.js

maybe i am not understanding promises, why am i getting redirected before functions.dioNic ends?
app.get('/dionic/scan', function(req, res) {
var functions = require("./functions.js");
var scrape = require("./scrapers/dionic.js");
//render index.ejs file
scrape.scrapeDio
// Remove duplicates in array data
.then(dedupe)
// Save
.then(functions.dioNic)
.then(console.log("Guardado"))
.then(res.redirect('/'));
});

You're calling these functions and passing their return values to then instead of passing the functions themselves:
.then(console.log("Guardado"))
.then(res.redirect('/'));
This is effectively the same problem as described here.
You can use bind to get a function with the argument bound:
.then(console.log.bind(console, "Guardado"))
.then(res.redirect.bind(res, '/'));
You could also use anonymous functions, if you don't like the look of bind:
.then( function () { console.log("Guardado") })
.then( function () { res.redirect('/') });

Related

Pass parameter to middleware function and return result

So guys, what I want to accomplish and not manage to get is to write a function that performs a query against database as a middleware, using req and res objects, and also can be used in a socket connection, to pass parameters to it and not use the req and res objects. Also I want it to return the result of the query. I tried using a middleware wrapper
function myFunc(param1, param2){
return (req, res) => {
here query
}}
works when hitting the endpoint with or without args i send, but dosnt work when i call the function from somewhere else
When you call myFunc you get returned a new function. You need to invoke that funcion, something like
myFunc('value1', 'value2')()
This will cause an error since you have no request or response object. These are express objects.
A better way to re-use the database code would be to put it in a function and inject that function to the middlewere.
E.g.
function getArticleById(id) {
return db.query('select * from articles where id = $1', [id])
}
Then create a middlewhere that takes this function in as dependency
function makeMiddlewere (getArticleById) {
return (req, res) => {
return articleById(req.query.id).then(articles => {
res.json(articles)
})
}
}
Then in your code you can do
Normal way
getArticleById(5).then(articles => {
console.log(articles)
})
Create the middlwere an
const express = require('express')
const getArticleById = require('./articlebyid')
const makeMiddlewere = require('./makemiddlewere')
const middlwere = makeMiddlwere(getArticleById)
const app = express.app
app.get('/article', middlewere)

How should I pass three arguments to an express/node arrow function?

I am declaring my function like this:
const parseConnections = (connectionsCSVPath, req, res) => {
//do a bunch of stuff
}
Inside the function, if I try to call res.locals.something, I get an error saying "cannot read property locals of undefined" I have tried several other syntaxes, such as this:
const parseConnections = ((connectionsCSVPath, req, res) => {
//do a bunch of stuff
})
this:
const parseConnections = (connectionsCSVPath, (req, res) => {
//do a bunch of stuff
})
and this:
const parseConnections = connectionsCSVPath, (req, res) => {
//do a bunch of stuff
}
and they all throw errors. What is the proper way to pass these 3 arguments to the function so that all 3 are defined inside?
Edit*: The function is then called like this:
router.post(
'/upload/engagements/batch', checkIfAuthenticated,
parseConnections('./tmp/connections.csv'),
parseMessages('./tmp/messages.csv'), (req, res) => {
//do a bunch of stuff
}
The problem is not (necessarily) with how you define the function but with how you are using it.
parseConnections('./tmp/connections.csv') calls the function right then and there. You are only passing a single argument to it, so req and res will be undefined.
function foo(a, b, c) {
console.log('a:', a);
console.log('b:', b);
console.log('c:', c);
}
foo('first argument');
However, you cannot pass values for req and res because these values are created and passed by express itself.
Essentially you are making the mistake of calling a function where you should be passing it. router.post expects to be passed one or more functions. But you are calling parseConnections and pass its return value instead which is probably undefined.
Here is a simple example that demonstrates the difference:
function foo(x) {
console.log('inside foo', 'x is ', x);
}
// bar expects to be passed a function that it can call
function bar(callback) {
console.log('bar received:', callback);
try {
callback(42);
} catch(e) {
console.error(e);
}
}
// this will work as expected
console.log('Passing a function');
bar(foo);
// this will error because `bar` doesn't receive a function.
// this is what you are doing
console.log('Calling a function and passing its return value');
bar(foo(21));
One way to fix your problem is to make parseConnections return a function, which is then received by router.post. I'm using normal function declarations here so that the syntax is not too confusing:
function parseConnections(connectionsCSVPath) {
return function(req, res) {
//do a bunch of stuff
};
}
This requires no changes to your router.post call.
Another solution is to pass a function to router.post that calls parseConnections instead, passing along req and res:
router.post(
'/upload/engagements/batch',
checkIfAuthenticated,
(req, res) => parseConnections('./tmp/connections.csv', req, res),
// alternatively you can use `.bind`:
// parseConnections.bind(null, './tmp/connections.csv'),
parseMessages('./tmp/messages.csv'), // <- this is likely wrong as well,
// but I leave this to you to figure out
(req, res) => {
//do a bunch of stuff
}
);

How exactly does a callback function work in nodejs?

I went through many examples of nodejs with callbacks, but didn't understand how exactly they work. I know that they're executed after the function, of which they're a part of is done, but I didn't understand callbacks as a function. Let me give an example:
function processData (callback) {
fetchData(function (err, data) {
if (err) {
console.log("An error has occured. Abort everything!");
callback(err);
}
data += 1;
callback(data);
});
}
Here, what how do callback(err) and callback(data) execute, as in, is it an alias for some other function to which we're passing the parameters - err/ data? If so, which function does it call when we write callback(parameter)?
Here's another example :
var express = require("express");
var bodyParser = require("body-parser");
var multer = require('multer');
var app = express();
app.use(bodyParser.json());
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './uploads');
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now());
}
});
var upload = multer({ storage : storage }).array('userPhoto',2);
app.get('/',function(req,res){
res.sendFile(__dirname + "/index.html");
});
app.post('/api/photo',function(req,res){
upload(req,res,function(err) {
//console.log(req.body);
//console.log(req.files);
if(err) {
return res.end("Error uploading file.");
}
res.end("File is uploaded");
});
});
app.listen(3000,function(){
console.log("Working on port 3000");
});
Again I sort of understood the other type of callback - suppose, here fs.readdir
fs.readdir(path, function (err, files) {
if (err) console.error(err);
for (var i = 0; i<files.length; i++) console.log(files[i];
}
console.log("done");
I know how this executes, most probably it'll print done first, and then prints the list of files, as soon as readdir is executed with files having the list of files.
But I didn't exactly understand the first and second code snippet. Can someone please explain that in simple terms, specific to the multer code snippet?
Callbacks and asynchronous behaviour are two different but related things. They're relate in the sense that asynchronous code can use callback to execute code in the intended sequence. But callbacks by themselves are just the result of the language treating functions as objects.
In your examples, you can see two sides of the callback story. One is the implementation and the other is the usage. The first code is an example of how to implement a function that accepts a callback. The last is an example of how to use a function that accepts a callback.
For simplicity let's ignore asynchronous behaviour for now and implement synchronous callbacks.
The following is an example of a function that loops through an array to construct a string:
function joiner (arr, callback) {
var return_value = "";
for (var i=0; i<arr.length; i++) {
return_value += callback(arr[i]); // use callback on each item
}
return return_value;
}
Once we've implemented the function above we can use it:
var result = joiner(['this','is','cool'],function(x) {
return "--> " + x + "!";
});
So, how do we know what arguments the callback accept? It accepts what the caller passes to it.
How do we know what arguments to pass to a callback function? We pass what the function is defined to accept?
If that sounds circular, it is. So how we really know what arguments to pass to a callback is:
If it's an API we created, we can do anything we want
If it's an API someone else created, read the documentation (or if your IDE can do it, check out the intellisense)
Callbacks gone meta
The above explanation is of course only the simplest example of callbacks: functions that accept a callback. The express example (and indeed your first example) shows how you can get even more complicated. You can write functions that accept a callback that accept a callback.
A simplified example of functions that accept a callback that accept a callback is something like this:
function processData (data) {
return "<" + data + ">";
}
function render (callback) {
return callback(processData);
}
So the render function above accepts a callback that will be passed a callback that returns a string. So if we now do:
var result = render(function(callback){
return "--" + callback('hello');
});
we will get:
--<hello>
As you can see, the callback we passed to render does not know how to process the data but render does (it calls processData). And render does not know what the data is but the callback we passed to it does ('hello'). Being able to pass a callback to a callback allows two pieces of code to cooperate without exposing each other's implementation details.
Async code uses this technique to order the sequence of code being executed. Because async code cannot return a value you can only pass it a callback so that once it gets its value your code (callback) can continue.
If you think about it, the Express API could have easily been implemented so that request handler callbacks must return the HTML page instead of calling res.send(). Indeed, this is how frameworks work in many languages. But that means that the request handler cannot execute asynchronous code to fetch data. Because Express doesn't want to limit you to using only synchronous code it passes an object with callback methods instead (res). This "design pattern" is called a monad. The one you see in Express is a continuation monad while the one you see in fs.readdir() is an I/O monad.
First, you got confused because you're not following the Node JS standards for passing parameters to asynchronous function. Every async callback must take 2 parameters. First should always be error and 2nd should always be data. Refer to how the callbacks for readdir and fetchData are defined.
So your code should ideally look like
function processData (callback) {
fetchData(function (err, data) {
if (err) {
console.log("An error has occured. Abort everything!");
callback(err, null); // here data will be null
}
data += 1;
callback(null, data); // here err will be null.
});
}
And you will define your callback function for processData at the time you call it like
processData(function(err, data){
err ? console.error(err) : console.log(data);
});
This way, in your callback, you know for sure whether the function has succeeded or failed.

Getting "Function: Bound" when trying to call a method

I'm using the Sails.js MVC and I'm trying to setup a service so I can make a call to an Active Directory server and pass the data for a user back to my controller.
I'm using some internal company modules for this which connect to our servers and pass back a user array with all the data for a selected user.
If I do this by making a function directly in the API controller it works fine, but when doing it by calling through a function from a separate file, rather than returning an array of [Function: bound].
Code from controller (LabController.js):
var adGet = require('../services/adGet');
module.exports = {
test: function (req, res) {
console.log(adGet.userData);
res.view({
user: adGet.userData
});
}
}
Code from the service (adGet.js):
module.exports = {
userData: function (req, res) {
var ad = require('active-directory');
ad.setCredentials({
user: 'username_removed',
password: 'password_removed'
});
ad.getUser(req.session.sisso.idsid).then(function (user) {
return (user);
});
}
}
Any help is greatly appreciated.
There's a few issues here.
First, you're trying to use return in your userData service method to return data, but it's an asynchronous function so that return statement is sending the data anywhere. You need to pass in a callback function as an argument to userData that can be called when the data is ready (i.e. when the database query returns):
module.exports = {
// note the new `cb` argument
userData: function (req, res, cb) {
var ad = require('active-directory');
ad.setCredentials({
user: 'username_removed',
password: 'password_removed'
});
ad.getUser(req.session.sisso.idsid)
.then(function (user) {
// Still nice to use `return` to make sure nothing
// gets executed after this statement, but it's the
// callback function that actually gets the data.
// Note the `null` first argument, indicating no errors.
return cb(null,user);
})
.catch(err) {
return cb(err);
});
}
}
Second, you're sending the adGet.userData function to your view as a local variable, but you're not actually calling it to get the data. And since it's an asynchronous function, you won't be able to call it from your view. You need to call it from within the controller and send the result to the view:
var adGet = require('../services/adGet');
module.exports = {
test: function (req, res) {
// Call service function, passing callback function in as argument
adGet.userData(req, res, function(err, user) {
// Handle errors
if (err) {return res.serverError(err);}
// If all goes well, send user data to view
return res.view({
user: user
});
});
}
}
Less importantly, you could refactor the userData method to not accept req and res as arguments--it's overkill. Save req and res for your controllers whenever possible. It would be better to have userData just expect userId and callback as arguments. Also, unless you've turned global services off using the config/globals.js file, you don't need to require the services file at the top of your controller; the adGet variable will be made available to you automatically.

Async call in node.js vs. mongoose

I have a node.js app that uses mongoose to connect to
a mongodb; i need to select all the documents inserted and i
i've problems with async stuff.
I've made a model with the following function:
exports.listItems=function() {
Ticket.find({}, function(err,tkts) {
console.log(tkts);
return tkts;
});
}
I correctly see the value of "tkts", but when i call it from:
exports.list = function(req,res) {
var items=db.listItems();
console.log("Items:"+items);
res.render('list', { title: title, items:items });
}
defined in app.js as:
app.get('/list', routes.list);
items is undefined (i think because of non-async definition of db.list()).
What am i doing wrong and how can it be corrected?
You need to use callbacks more appropriately.
A more traditional listItems function would be
exports.listItems = function(done) {
Ticket.find({}, done);
}
Then, in list, you could do:
exports.list = function(req,res) {
db.listItems(function(err,items){
console.log("Items:"+items);
res.render('list', { title: title, items:items });
});
}
Because of the asynchronous nature of Node.JS, you should always pass (and expect) a callback in your functions. So that you can defer execution if something asynchronous is executed.
Also: be sure to check out async, its an insanely good and easy-to-use library, that will simplify complex async scenarios in a breeze.

Resources