passing variables through callback functions in node.js - javascript

I posted this question yesterday but I guess I just confused everyone. I got responses like "what exactly is your question?" So I am expanding and reposting today.
The following node.js snippet is from the file "accounts.js" which is in an ETrade api library that exists in the path /lib. It should return json containing data about the accounts of the authenticated user. The authentication part is working great. I'm confused about what exactly is being done in the last line of this function:
this._run(actionDescriptor,{},successCallback,errorCallback);
Ten years ago (the last time I was coding), we didn't have the construct "this" and I haven't a clue about "_run" and Google searches have not been helpful. Here is the function.
exports.listAccounts = function(successCallback, errorCallback) {
var actionDescriptor = {
method: "GET",
module: "accounts",
action: "accountlist",
useJSON: true,
};
this._run(actionDescriptor, {}, successCallback, errorCallback);
};
I understand that the function is accessed with "et.listAccounts ...." but then my understanding goes all to hell. It's pretty obvious that a get is being executed and json data returned. It's also obvious that the result is passed back through the successCallback.
In my app.js file, I have the following:
var etrade = require('./lib/etrade');
var et = new etrade(configuration);
Can someone please suggest a snippet to be used in app.js that will output the accounts data to the console?
It seems like the json data must be passed back through the successCallback but I'm lost on how to access it on the app.js side.
Suppose in app.js I want to put the accounts data in a variable called myAccounts. The exports.listAccounts function does not specify a return value, so I doubt I can do var myAccounts = et.listAccounts(). Likewise, myAccounts will be undefined if I try to do this: et.listAccounts(){myAccounts, error}. Finally, the listAccounts function contains two possible variable names I could use, "accounts" and "accountlist" but these turn out to be undefined at app.js.
When I put a function in successCallback in app.js to write a generic message to the console, the message appears in the log so I know I am making it into the listAccounts function and back successfully. In this case, the log also shows
"Request: [GET]: https://etwssandbox.etrade.com/accounts/sandbox/rest/accountlist.json"
From this I deduce that the data is actually being returned and is available at that end point.

Ten years ago (the last time I was coding), we didn't have the construct "this" and I haven't a clue about "_run"
this refers to the current object, further reading here. _run is just what they chose to call the function.
I have no experience with this module, but with a cursory glance at the git repo I suspect you will want to expand your app.js like so:
et.listAccounts(function(response) {
console.log(response);
});
In javascript functions are first order and so can be passed around like variables see here. listAccounts wants a function passed to it, and when it is complete it will call it with one parameters, as can be seen in etrade.js.
There is also the function errorCallback which is much the same but is called on an error. You could expand the above snippet like so:
et.listAccounts(function(response) {
console.log(response);
}, function(error) {
console.log(error);
});

Related

NodeJS : how to use arguments like req, res, result in functions?

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.

yeoman generator: copy or template doesn't work from inside async callback

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.

Deps autorun in Meteor JS

Decided to test out Meteor JS today to see if I would be interested in building my next project with it and decided to start out with the Deps library.
To get something up extremely quick to test this feature out, I am using the 500px API to simulate changes. After reading through the docs quickly, I thought I would have a working example of it on my local box.
The function seems to only autorun once which is not how it is suppose to be working based on my initial understanding of this feature in Meteor.
Any advice would be greatly appreciated. Thanks in advance.
if (Meteor.isClient) {
var Api500px = {
dep: new Deps.Dependency,
get: function () {
this.dep.depend();
return Session.get('photos');
},
set: function (res) {
Session.set('photos', res.data.photos);
this.dep.changed();
}
};
Deps.autorun(function () {
Api500px.get();
Meteor.call('fetchPhotos', function (err, res) {
if (!err) Api500px.set(res);
else console.log(err);
});
});
Template.photos.photos = function () {
return Api500px.get();
};
}
if (Meteor.isServer) {
Meteor.methods({
fetchPhotos: function () {
var url = 'https://api.500px.com/v1/photos';
return HTTP.call('GET', url, {
params: {
consumer_key: 'my_consumer_key_here',
feature: 'fresh_today',
image_size: 2,
rpp: 24
}
});
}
});
}
Welcome to Meteor! A couple of things to point out before the actual answer...
Session variables have reactivity built in, so you don't need to use the Deps package to add Deps.Dependency properties when you're using them. This isn't to suggest you shouldn't roll your own reactive objects like this, but if you do so then its get and set functions should return and update a normal javascript property of the object (like value, for example), rather than a Session variable, with the reactivity being provided by the depend and changed methods of the dep property. The alternative would be to just use the Session variables directly and not bother with the Api500px object at all.
It's not clear to me what you're trying to achieve reactively here - apologies if it should be. Are you intending to repeatedly run fetchPhotos in an infinite loop, such that every time a result is returned the function gets called again? If so, it's really not the best way to do things - it would be much better to subscribe to a server publication (using Meteor.subscribe and Meteor.publish), get this publication function to run the API call with whatever the required regularity, and then publish the results to the client. That would dramatically reduce client-server communication with the same net result.
Having said all that, why would it only be running once? The two possible explanations that spring to mind would be that an error is being returned (and thus Api500px.set is never called), or the fact that a Session.set call doesn't actually fire a dependency changed event if the new value is the same as the existing value. However, in the latter case I would still expect your function to run repeatedly as you have your own depend and changed structure surrounding the Session variable, which does not implement that self-limiting logic, so having Api500px.get in the autorun should mean that it reruns when Api500px.set returns even if the Session.set inside it isn't actually doing anything. If it's not the former diagnosis then I'd just log everything in sight and the answer should present itself.

Implementing logging in Metro Application developed using Html/WinJS

I need to provide with error logging in my Windows 8 Metro application developed in Html/WinJS
so that user can get to know what went wrong from a log file located in the app's local folder.
I have checked WinJS.log(message, tags, type); which will write to the console but not able to find anything via which i can get it on a local file.
What is the best way to do the same and if there are any 3rd party libraries/js available for error logging in metro applications developed in WinJS ?
Thanks in advance.
WinJS.log is just a placeholder. Without proper initialization it does nothing (in fact, it's not set at all). If you just call WinJS.Utilities.startLog() at your application startup, it defaults to wiring up a logger for the console.
If you want something more complete, you'll need to build it. I've built a small sample below.
function startFileLog() {
// choose where the file will be stored:
var fileDestination = Windows.Storage.ApplicationData.current.localFolder;
var logger = new WinJS.Promise(function (complete) {
var logfilename = new Date().toISOString().replace(/[:-]/g, "");
logfilename = "log-" + logfilename + ".log";
fileDestination.createFileAsync(logfilename,
Windows.Storage.CreationCollisionOption.generateUniqueName)
.done(function (file) {
complete(file);
});
});
var actionFn = function (message, tag, type) {
logger.then(function (file) {
var m = WinJS.Utilities.formatLog(message, tag, type);
Windows.Storage.FileIO.appendTextAsync(file, m).done();
});
};
WinJS.Utilities.startLog({ action: actionFn });
}
By calling the startFileLog function above, it creates a new log file (by using the current Date/time as part of the file name) within a promise. Then, a function called actionFn is passed to the startLog function. By passing an optional property of the options named action, the default "write to console" behavior is overwritten (if you didn't want it overwritten, you could call startLog without the action, then copy the function reference from WinJS.log and replace it with your own function, and call it as well). When the log function is called, it now calls actionFn which uses the promise created earlier to verify that the log file is in fact available for writing before continuing. If it's not ready yet, it will be queued. So, this means that even though the file may not be ready immediately, the log will, in the end, contain the results you'd expect. There would be a short period of time where, due to async nature of WinJS, if the application crashed before the file completely opened, that logged items will be missed. You could delay the application startup if you wanted until the file was opened by returning the logger promise:
function startFileLog() {
/// ... etc..
return logger;
}
startFileLog().then(function() {
// the application can now be assured that the log file is ready to accept
// writes ... (but again, it's all async, so a write may be missed in
// extreme cases)
});
You'd likely want to create a function at the end of your application to clean/close the log file.

extjs commit answer from server to store

I try to develop my own proxy (extends Ext.data.proxy.Proxy). after getting the response from my server (json) I can decode it and using my Json reader I create the set of records to commit to the store. As I understand I have to create an Operation object and commit the data with operation.commitRecords(). But this line if (clientRecords && clientRecords.length) never passes as clientRecords is undefined. I cannot understand how to use this object neither how to initialize it. This is my current code:
options.action = options.action || 'read';
var operation = new Ext.data.Operation(options);
var json = reader.read(response);
operation.commitRecords(json.records);
What should I do in order to commit the records?
Thanks in advance!
That's not the proxy that is supposed to create the operation, it is the store. Then it passes this operation to the proxy, along with a callback. The proxy executes the operation in its own way and, when it is done, use the callback to notify the store.
So, the short answer to your question is that you need to execute the callback passed to the CRUD method you're implementing. Let's say it's read (Amit's right that some context about your code could have helped). So that would be something like:
// see, the operation is given to us
read: function(operation, callback, scope) {
var me = this;
doWebsocketMagic(function() {
// notify the store
Ext.callback(callback, scope || me, [operation]);
});
}
Now, that won't be enough, because proxies are expected to manipulate the operation. You'll have to read the code from other proxies to know how. ServerProxy would have saved you that work, but you're not working with HTTP requests ("ServerProxy should ideally be named HttpProxy"). No luck.
So you should start by read the code of the read method of MemoryProxy. It offers, in one single place, an example of everything (I think) that you've got to do.
Then, maybe you can go clever about it:
Ext.define('My.WebsocketProxy', {
extend: 'Ext.data.proxy.Memory'
,read: function(operation, callback, scope) {
var me = this;
doWebsocketMagic(function(response) {
me.data = response;
Ext.data.proxy.Memory.prototype.read.call(me, operation, callback, scope);
});
}
});
Ok...Having searched how the store initializes the operation then it calls the proxies read method with callback function onProxyLoad(operation);. So adding store.onProxyLoad(operation); populates the store properly. Thanks for the replies guys...they helped solving my problem.

Categories

Resources