I've gotten some third party asynchronous functions to work with Koa through thunking, either by wrapping the function like so: var thunkedFunction = function(params) { return function(callback) { originalFunction(params, callback) }; ) or using the node thunkify library.
However when I try this with ntwitter's stream like so:
var streamThunk = thunkify(tw.stream);
var stream = yield streamThunk("statuses/filter", {track: track});
I get the following error: "Cannot read property stream_base of undefined".
Digging deeper into ntwitter (built on node-twitter) I see that the Twitter.prototype.stream function calls this.options.stream_base, and this.options is defined when I call it normally i.e. tw.stream(function(stream) {...}); but undefined when I thunk the function. Is there any reason that the function loses its scope when thunked, and is there a way to circumvent this?
Notice that thunkify doesn't see the tw object. So, the way it's designed, it has no way of knowing the context (tw in your case) of the function it's getting (tw.stream).
The function returned by thunkify will pass along whatever this context it's called with (source: node-thunkify/index.js).
This means you should be able to change the second line in your example to:
var stream = yield streamThunk.call(tw, "statuses/filter", {track: track});
Read more about call.
Related
I'm fairly new to JS especially Node and Express. I am following some tutorials on how to build an API and at the same time learning about JS special features such as let/const/var, arrow functions etc.
In many tutorials I have seen things likes this :
somecode.then((result) => {someothercode})
With: "somecode" being for example a get request
Is "result" the name of the returned value or is it a convention that JS developper use?
By that I mean, does this for example work?
somecode.then((foo) => {someothercode})
Also for req, res variables what does this mean?
app.get("/users/:userId", [
usersController.getById
]);
Here is the getById function (using once again the "result"):
exports.getById = (req, res) => {
userModel.findById(req.params.userId).then((result) => {
res.status(200).send(result);
});
};
the getById method defined in the controller needs (req, res), does that mean, when i call it like the code above, the req and res arguments are implicitly used?
Also it needs a parameter :
req.params.userId
which is in the url of the route, how does it pass to another file?
I have a route.js file that uses a controller.js file that uses a model.js. How does the param go from route to controller?
And it won't work if I change the param name right? for example:
req.params.id
Sorry for long post, I'm trying to understand JS logic to get some good habits and write clean code.
Thanks!
Is "result" the name of the returned value or is it a convention that JS developper use? By that I mean, does this for example work?
From my experience, yes - result is often used. Often times you'll see thing like value, response, but ultimately it can be whatever you define. I would recommend sticking to convention, and also check out the MDN Promise tutorial if you are starting out with understanding NodeJS asynchronous operations.
Also for req, res variables what does this mean?
app.get("/users/:userId", [
usersController.getById
]);
That is a middleware chain. Check out the Express docs for more information.
the getById method defined in the controller needs (req, res), does that mean, when i call it like the code above, the req and res arguments are implicitly used? Also it needs a parameter :
req.params.userId
which is in the url It won't work if I change the param name right? for example:
req.params.id
Yes, that is using a named parameter. Without the full router code, it is hard to know how the getById method is linked to the defined route. The Express routing documentation will likely be a good start on that.
Is "result" the name of the returned value or is it a convention that JS developper use?
result is the name of a new variable you are creating to represent the value passed in from the Promise resolution. Yes, your foo example will work.
(req, res) => {} is the same (mostly) as a function that looks like this:
function getById(req, res) {...}
req, and res are just representational of the values that will be passed to this function. They could just as easily have been called (foo, bar).
It looks like you're struggling with understanding callback functions. Consider the following code then please crack open the source code for the packages that you are using. and it looks like you are using express.js
function something(callback) {
var x = 5;
var y = 'anything';
callback(x, y);
}
something(function(req, res) {
console.log(req);
console.log(res);
});
the something function is created and inside of that function scope, var x and y are created with any type. then when we invoke or use something function we are passing a function as a variable that gets passed in as variable callback then it can be used since it is a function so we call callback with x and y which can literally be any value and for effect, I am passing back a number and a string as req and res.
It's just a convention. Note that the code:
somecode.then((result) => {someothercode});
Is actually:
somecode.then(myFunction);
Since somecode is a Promise, your function may be called with zero or one argument. It is up to you to name this argument:
function myFunction (foo) {
// use foo here
}
somecode.then(myFunction);
Of course, unlike some other languages, javascript does not force you to name your function. You can just use a nameless (anonymous) function:
somecode.then(function(mango) { /* use mango here */ })
Arrow functions is a new syntax allowing you to write anonymous functions in a shorter style (it also behaves slightly differently with regards to scope and the value of this)
Express.js and http.Server
In node's http.Server library and Express.js framework, each server request will call a function you define and pass it two arguments: the request object and the response object. The variables req and res are just conventions people use when writing their own request handler functions. You can name them anything you like. For example you may prefer to use request and response instead or rx and tx:
app.get('/say/hello', (rx, tx) => tx.send('Hello'));
How many arguments do I write a callback function with??
The best way to know is to read the documentation of the module you are using. It is not the only way to know - you can of course read the source code instead. But it is often easier to read the documentation. Because of this, javascript modules tend to have really good documentation (otherwise they would be unusable and ignored by the community).
Express.js will actually pass three arguments to your callback (not two!!) - request, response and next where next is a function you can call if you want Express to continue processing instead of replying to the request. One interesting feature of javascript is that you are allowed to call functions with fewer or more arguments and it is not considered a syntax error:
function example (x) {}
example(); // not an error
example(1); // not an error
example(1,2,3,4); // also not an error
Express uses this feature by always calling your callback with three arguments while allowing you to declare said callback with only two arguments if you don't need the third, next argument.
I need to load untrusted modules, written by third parties. I'm using vm for the sandbox and I was thinking to use threads (from npm: here) in order to load the module asynchronously and avoid blocking code.
I have stripped down the code to the minimum, because I'm stuck and I dont' understand if what I'm trying to achieve is impossible or it's just me messing with scopes.
Here is a dummy module:
exports.dummy = function () {
console.log('Dummy');
};
exports.val = 5;
And here is a module where I try to load this dummy module using threads:
var spawn = require('threads').spawn;
var mod;
var other;
var t = spawn(function (input, done) {
var m = require(input.dir + '/dummyMod');
done({m: m, other: 'hey'})
});
t.send({dir: __dirname}).on('message', function (result) {
mod = result.m;
other = result.other;
console.log(mod);
console.log(other);
t.kill();
});
The logged output is:
{ val: 5 }
hey
As you can see, the function in the dummy module has been skipped. If I try to load the module in the main process and log it, then the function is, of course, part of the object.
You need to properly serialize and deserialize the function. JSON.stringify ignores functions, probably because json is a format for storing data, not scripts.
Serialize the function by calling toString() on it. Then you can send it along as a string.
done({m: m.toString(), other: 'hey'})
Converting m to a string will give you something like this:
"function m(){console.log(\'called m()\')}"
On the receiving end, you will need to deserialize the function.
var m = new Function("return " + result.m)()
I am trying to use a library found on the web, called himalaya, which is an html parser.
https://github.com/andrejewski/himalaya
I followed their guide on importing the library, so I do
var himalaya = require('himalaya');
However when I call one of its member functions, I get an error
TypeError: himalaya.parse is not a function
I tried executing himalaya.parse() on the web browser console directly, it works. I tried commenting out the require statement in the js file, the function no longer works on web browser.
I guess this implies the require statement works? But for some reasons I cannot use it in my javascript file, only on the browser console.
Perhaps something with file scopes? Here is part of my code.
var himalaya = require('himalaya');
Template.main.onCreated(function () {
var http = new HttpGet("www.someurl.com/", "/somedirectories/", function (response) {
console.log(himalaya.parse(response.content));
});
http.sendRequest();
});
I am certain that response.content does contain a valid html string.
When you call the himalaya.parse inside the main.onCreated function it seems like the library is not completed loaded at that time. That's why it only runs in your browser console. Check if the himalaya library has a onReady function to let you know exactly when you can use it. If not, you can:
a) Call the parse function inside the main.onRendered or
b) Keep the parse call inside the main.onCreated and set a timeOut to call it after a half second like this:
var himalaya = require('himalaya');
Template.main.onCreated(function () {
var http = new HttpGet("www.someurl.com/", "/somedirectories/", function (response) {
setTimeout(function(){himalaya.parse(response.content)},500);
});
http.sendRequest();
});
If you have an issue with the setTimeout check this answer:
Meteor.setTimeout function doesn't work
Inside a yeoman generator I am trying to do a conditional copy depending on the state of an external network resource. My problem is that the yeoman copy command (src.copy and template too for that matter) does not seem to do anything when invoked inside of an async callback, such as one from a http request.
Example code, inside of the yeoman.generators.NamedBase.extend block:
main: function(){
//-> here this.copy('inlocation','outlocation') works as expected
var that = this;
var appName = ...
var url = ...
var req = http.request(url, function(res){
//-> here that.copy('inlocation','outlocation') DOES NOT work
res.on('data', function (data) {
//console.log('Response received, onData event');
//-> here that.copy('inlocation','outlocation') DOES NOT work
});
//-> here that.copy('inlocation','outlocation') DOES NOT work
});
req.on('error',function(error){
//...
});
req.end();
//-> here this.copy('inlocation','outlocation') works as expected, once again
Note the locations marked by '//-->' comments for points of reference - when it works, it works as expected. When it doesn't, there's no output on console whatsoever (so that.copy seems to exist as a function, in fact I can assert that typeof that.copy === 'function' !), no error messages, just no file created (the usual file create message is missing too which is a characteristic of the properly working command).
Using call or apply to pass an explicit this reference to the functions didnt change the behaviour, nor did binding this to the async functions.
What is the explanation to this behaviour, and how can I make copy calls in this async manner?
As per Eric MORAND's comment, I'll post the solution I found as a separate answer, instead of an edit to the original post, hopefully it'll be easier to find:
I've found a solution, using the async() function of the yeoman RunContext. (see the api docs here) The following line at the beginning of the async code:
var done = this.async();
then a call to done() right before I wanted to run copy made it behave as originally expected.
I am new to both node.js and promise style function call. By looking at an denodeify example at http://runnable.com/Ulatc0QnzUgUAAAK/adapting-node-js-with-q-for-promises, I am trying to denodeify the methods of the node.js node-ftp module as following:
var ftp = require('ftp');
var q = require('q');
var ftpClient = new ftp();
ftpClient.on('ready', function() {
var ftpList = q.denodeify(ftpClient.list);
ftpList().then(function(list) {
console.log(list);
}.then(null, function(err) {
console.log(err);
}).done(function() {
ftpClient.end();
});
});
ftpClient.connect();
However, when running that code with node, it shows the error "list error: TypeError: Object # has no method '_pasv'"
I am not sure what's wrong with that piece of code. Does anyone know what's wrong with that? Can you point me some way to debug/troubleshoot the cause of that error message?
Thanks.
When you pass
ftpClient.list
to Q.denodefiy, you are getting the function object, list from the ftpClient object. It will be just a function and the relationship with the parent is lost. This is important because, the bound function list might be dependent on the ftpClient object. So, you must make sure that link is not broken.
Quoting from the Q.denodeify docs,
Note that if you have a method that uses the Node.js callback pattern,
as opposed to just a function, you will need to bind its this value
before passing it to denodeify, like so:
var Kitty = mongoose.model("Kitty");
var findKitties = Q.denodeify(Kitty.find.bind(Kitty));
The better strategy for methods would be to use Q.nbind, as shown below.
So, you can fix your code in two ways,
Using Q.denodeify and Function.prototype.bind, like this
var ftpList = q.denodeify(ftpClient.list.bind(ftpClient));
Using Q.nbind, like this
var ftpList = q.nbind(ftpClient.list, ftpClient);
you need to use q.nbind
q.nbind(ftpClient.list, ftpClient);