Actually I am reading some codes in Nodejs, but I cannot get what it means because some common use of function that I don't quite understand. Can someone tell me what the function(done) means? It is a callback or something? in the js file I can't see the definition of done. Thanks
Andes
var getRedisUri = exports.getRedisUri = function (done) {
if (process.env.CLUSTER_CONFIG_URI) {
return done(null, process.env.CLUSTER_CONFIG_URI);
}
//redis uri
Fs.readFile(
'/opt/redis_uri',
{encoding: 'utf8'},
function (err, redis_uri_data) {
if (err) {return done(err);}
var redis_uri = redis_uri_data.toString();
if (process.env.NODE_ENV !== 'production' &&
process.env.VM_DOMAIN &&
(redis_uri.indexOf('localhost') !== -1 || redis_uri.indexOf('127.0.0.1') !== -1)) {
redis_uri = redis_uri.replace('localhost', process.env.VM_DOMAIN);
redis_uri = redis_uri.replace('127.0.0.1', process.env.VM_DOMAIN);
}
done(null, redis_uri);
});
};
That line is just the start of a function definition. function(done) just means that when this function is called, it should be called with one argument, and during the execution of the function, that argument will be referred to as done.
More specifically, I think it has to do with middleware (are you using express.js here?). If you have a bunch of middleware functions in express, the express router will call those functions for you, and pass in a function as an argument which, when called, passes the request to the next middleware function. It seems like that's what's happening here, as done gets called at the end of the function.
Node.js has stuff like this because it's all asynchronous, so you can't use return statements for much.
Related
Basically I want to instead of this...
app.get(routes.test, function(req, res, next){
actualRouteHandler(req, res, next) // Always returns a promise or throws.
.catch(function(err) {
next(err);
});
});
Have this:
app.get(routes.test, catchWrap(actualRouteHandler));
Or something similar, I've tried messing around with fn.apply and things but I can't find a way to pass actualRouteHandler the correct parameters (req,res,next) and still have the function. Do I need to return a function or something similar?
Edit: I think there may be libraries that do this but we have no access to the actual express application in this bit of code.
In your specific case, catchWrap would look like this:
function catchWrap(originalFunction) {
return function(req, res, next) {
try {
return originalFunction.call(this, req, res, next);
} catch (e) {
next(e);
}
};
}
That returns a new function that, when called, will call the original with your catch wrapper around it. The key parts are that it creates and returns a function (return function(req, res, next) { ... };) and this line:
return originalFunction.call(this, req, res, next);
Function#call calls the given function saying what to use as this during the call (in the above we pass on the this we recieved) and the arguments to use in the call.
You'd use it as you showed:
app.get(routes.test, catchWrap(actualRouteHandler));
Or if you prefer to define the actual handler as an anonymous function:
app.get(routes.test, catchWrap(function(req, res, next) {
// ...handler code here...
}));
That catchWrap is specific to your situation, because you want to call next(e) with the exception if thrown. The generic form of "wrap this function in another function" is like this:
function catchWrap(originalFunction) {
return function() {
try {
// You can do stuff here before calling the original...
// Now we call the original:
var retVal = originalFunction.apply(this, arguments);
// You can do stuff here after calling the original...
// And we're done
return retVal;
} catch (e) {
// you can do something here if you like, then:
throw e; // Or, of course, handle it
}
};
}
arguments is a pseudo-array provided by JavaScript that includes all of the arguments that the current function was called with. Function#apply is just like Function#call, except you give the arguments to use as an array (or pseudo-array) rather than discretely.
Taking it a step further: You could create a function you passed a router to that returned an updated router that auto-wrapped the handlers when you called get, post, etc. Something along these lines (untested):
const routeCalls = ["all", "get", "post", "put", "delete", /*...*/];
function patchWithCatcher(router) {
for (const call of routeCalls) {
const original = router[call];
// This seemingly-pointless object is to ensure that the new function
// has a *name*; I don't know whether Express relies on the name of
// the function you call (some libs with similar APIs do).
const rep = {
[call]: function (path, ...callbacks) {
return router[call](path, ...callbacks.map(catchWrap));
},
};
router[call] = rep[call];
}
return router; // Just for convenience
}
Using it:
const app = patchWithCatcher(express());
app.get(/*...*/);
app.post(/*...*/);
// ...
or
const router = patchWithCatcher(express.Router(/*...options...*/));
router.get(/*...*/);
router.post(/*...*/);
// ...
That monkey-patches the router you pass in. I considered using the passed-in router as the prototype of a new object in order to avoid modifying the router object passed in, but you'd have to test that really, really thoroughly, there are lots of edge cases around this during calls. Another viable alternative would be to use a Proxy, though at a runtime cost.
The following simple approach can be used to setup a generic error handler -
Step 1: Write the error handler after registering your routes. So, for example, if you are registering your multiple routes like this:
config.getGlobbedFiles('./app/routes/modules/**/*.js').forEach(function (routePath) {
require(path.resolve(routePath))(app);
});
After registering the routes, you can setup a generic error handler -
app.use(function (err, req, res, next) {
// If the error object doesn't exists
if (err != undefined) {
//do error handling
}
else {
//no error found
}
});
Step 2: In the route handler, you need to make sure that you call "next()" if no error is found. In case of error, you need to call "next(err)".
The code is from the IPFS (Inter-planetary file-system) HTTP API JS implementation: https://github.com/ipfs/js-ipfs-api/blob/master/src/api/add.js
'use strict'
const Wreck = require('wreck')
module.exports = (send) => {
return function add(files, opts, cb) {
if (typeof(opts) === 'function' && cb === undefined) {
cb = opts
opts = {}
}
if (typeof files === 'string' && files.startsWith('http')) {
return Wreck.request('GET', files, null, (err, res) => {
if (err) return cb(err)
send('add', null, opts, res, cb)
})
}
return send('add', null, opts, files, cb)
}
}
The function being described is the add() function, used to push data to IPFS.
I'll start by explaining what I do understand: the add() function takes three arguments – if there is no options object (the user omitted it) and it's been replaced by a function: the user is trying to implement a callback function instead – change the callback to opts; cb = opts.
Secondly, if the cited file is a text file && starts with http – it's obviously hosted remotely and we need to fetch it using Wreck.
All of this I understand, but why are we using the (send) => arrow function? Why do we use return function add...? What's the send('add', null, opts, res, cb) and return send('add', null, opts, res, cb) used for? How is the callback (cb) implemented? Help me understand what's happening here
The whole thing being exported is a function, which expects send as an argument; this lets the calling code do dependency injection by passing in the send function to use. It's expecting to be used something like this:
let addBuilder = require("add");
let add = addBuilder(senderFunction);
// This function ----^
// is `send` in the `add.js` file.
// It does the actual work of sending the command
// Then being used:
add(someFiles, someOptions, () => {
// This is the add callback, which is `cb` in the `add.js` file
});
(Frequently, the first two statements above are written as a single statement, e.g. let add = require("add")(senderFunction);)
Basically, the whole thing is one big builder for something that uses a given send function, which is injected into it by calling the builder. That way, it can be tested by injecting a testing version of send, and used for real by injecting a real version of send; and/or various "real" versions of send can be used in different environments, transport mechanisms, etc.
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.
I'm trying to return boolean answer from function and want to check with if-else statements.
function dnsCheck(domain,tld) {
var dns = require('dns')
dns.lookup(domain+'.'+tld, function (err, addresses) {
if (err) return false // means domain not registered
else return true // means domain registered
})
}
my conditional statement:
if(domain_validator(domain,tld) && dnsCheck(domain,tld)) {
res.end("avl")
}
else {
res.end("not avl")
}
The first function alone works in the if statement but when I add 2nd function "dnsCheck", it fails to work as expected. Am I missing something?
Rewrite dnsCheck like this:
function dnsCheck(domain, tld, callback) {
var dns = require('dns')
dns.lookup(domain + '.' + tld, function(err, addresses) {
callback(err == null);
});
}
Then call it like this:
dnsCheck(domain, tld, function(isValidDns) {
if (isValidDns) {
// Profit...
}
});
It's likely that the other function domain_validator is/should be async too.
Not all functions are async, but if you want to know if it is, then the signature of the function will (generally) have a function as the last parameter, with (usually) a signature of function(err, result), as dns.lookup does.
Try reading about node.js callbacks. This is one such explanation:
http://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks
So I have started with express.js - my first JS web dev framework. I didn't make anything small, but started a bigger project. I'm learning, and building at the same time.
Coming from a Python/Flask background, express seems very complicated.
Like in python, if I want a helper method, I can just put it on top of the file, or in a new module, and import it. Super easy. But in node/express, things are async, and everything is in exports or module.exports (??). Where do helper methods go? How do I call them with callbacks?
In another question I asked, I was doing the same kind of computation multiple times. In Python, I would write a method (with if statements and parameters), and call it multiple times, using a for.. in... loop. The code I have right now is very redundant.
How do I do it in express? What are best practices for writing express code?
It really depends of what your helper is doing. If it operates with data which is passed as a parameter to it then you may save it in an external module and use require to access it.
// helpers/FormatString.js
module.exports = function(str) {
return str.toUpperCase();
}
// app.js
var formatter = require("./helpers/FormatString");
However, if you need to modify the request or the response object then I'll suggest to define it as a middleware. I.e.:
app.use(function(req, res, next) {
// ... do your stuff here
});
#Krasimir gave a correct answer. Regarding your question how to deal with asynchronous helper functions I can give you an example (not the usual foo/bar, but one of my own helper functions):
var cached; // as modules can act like singletons I can share values here
module.exports = function fetchGoogleCerts(options, callback) { // <-- callback
var now = Date.now();
if (ttl > now && cached) {
callback(null, cached); // <-- return with success
return;
}
request({
uri: options.certsUrl || defaultCertsUrl,
strictSSL: true
}, function (err, response, body) {
var error, certs;
if (!err && response.statusCode === 200) {
certs = jsonParse(body); // a local function
if (!certs) {
callback('parse_error', null); // <-- return an error
return;
}
cached = certs;
// ... more code
callback(null, cached); // <-- success case
} else {
error = {
error: err,
statusCode: response.statusCode
};
log.error(error, 'error fetching google certs');
callback(error); // <-- return an error
}
});
}
};
And using the helper:
fetchGoogleCerts = require('./google-certs.js'),
module.exports = function fetchCertsAndDecodeIdToken(token, options, callback) {
fetchGoogleCerts(options, function (err, googleCerts) {
if (err) {
callback({
errorCode: codes.io_error,
errorMsg: 'Unable to fetch Google certificates',
error: err
});
return;
}
decodeAndVerifyGoogleIdToken(googleCerts, token, options, callback);
});
};
As you can see above, the simple solution is to provide a callback function to your asynchronous helper function.
Of course you can also export an Object which extends EventEmitter, then you might not need a callback function, but register for the events. Here is an Example for a helper which emits events.