Pass extra argument into callback - node.js

Say I have a function with a callback:
client.on("foo", (arg1, arg2, arg3) => {
// ...
client.bar();
} );
Now I want to seperate the callback into a different module:
const eventHandler = require('./eventHandler.js');
client.on("foo", eventHandler.foo);
I now need access to the clientvariable in the eventHandler.js module, because I need to call client.bar();. How would I go about passing client into the eventHandler.foo function?

You can create an inline callback and call the event handler with any arguments you want.
Example:
client.on("foo", () => eventHandler.foo(client));
However, looking at your code, it appears that the on event is a property on the client itself. Can't you just edit the code of client and make it pass itself as an argument to the callback function?

Related

Node Js passing parameters to an "off the shelf" callback

I have a library that makes a web request and passes the results to a callback like the sample below:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, callback);
Then the callback is something like:
var callback = function (data) {
console.log(data);
};
Is there a way I can pass more arguments to the callback from the caller given this is an off the shelf ready library?
just define your "first" callback inline, there you can pass more parameters:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
callback(data, otherVar1, otherVar2);
});
Then your callback could look something like this:
var callback = function (data, otherVar1, otherVar2) {
console.log(data);
console.log(otherVar1);
console.log(otherVar2);
};
Without editing the code of the library that calls the callback, you can't change what is passed to that specific callback. But, there are multiple options where you can get access to other variables:
Inline callback accessing variables in scope
you can use scope and an inline definition to make other variables available (via scoping) to the callback.
let someVar1 = "foo";
let someVar2 = 4;
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
// can access data, someVar1 and someVar2 here as they are all in scope
console.log(someVar1);
});
Use .bind() to add arguments to the callback
You could also use .bind() to create a new function that adds parameters.
function callback(someVar1, someVar2, data) {
console.log(someVar1);
}
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, callback.bind(null, "foo", 4));
.bind() creates a new function that adds additional arguments before calling the actual callback() function.
Make your own stub function
Or, make your own stub function that calls your callback (essentially what the .bind() option was doing for you):
function mycallback(someVar1, someVar2, data) {
console.log(someVar1);
}
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
mycallback("foo", 4, data);
});
I assume your callback arguments are variable and that the function is defined in an isolated manner (where it can't access scope beyond itself, and that you don't want to mutate the callback function's underlying data. It also assumes that Data is an object (You should perform some validation on this though). Therefore you could do something like the following:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
data.newArgument = 'lorem';
callback(data);
});
This way you can add any arbitrary arguments onto the data object passed.

Nodejs Callback: How to define a Callback?

I am reviewing a sample nodejs server code, which is working fine. but could not understand the following code:
var handlers = {};
// Creating a sample handler
handlers.sample = function(data,callback){
callback(406,{'name':'sample handler'}); // How is this line of code working??
};
// Creating a not found handler
handlers.notFound = function(data,callback){
callback(404); // How is this line of code working??
};
In the entire code there is no implementation of "callback" function then how
callback(406,{'name':'sample handler'});
and
callback(404);
are working?
Please suggest. Thanks.
callback isn't implemented in the code you posted; it's a named parameter.
To call one of the functions that requires a callback, you'll need to pass it in as an argument, like:
handlers.sample("some data", () => console.log("I'm in a callback!));
The first argument ("some data") goes in the data parameter, and the second argument (() => console.log("I'm in a callback!)) goes in the callback parameter. When callback(404) is run, it executes the callback function (in the above example, it would do console.log("I'm in a callback!)).
Basically, when you call handlers.sample, you should pass a function as your second argument, and that function will be called, usually asynchronously, and presumably after something is done with the data you pass as the first argument. For example, based on the code you provided:
handlers.sample(dataObject, (number, object) => {
console.log(number)
console.log(object.name)
})
would yield this result in the console:
406
sample handler
I’m curious if this is a public library you are seeing this code in, so we can take a closer look?
I did a further digging in into this and found that
selectedHandler
in the following code (this is not mentioned in the question) is getting resolved into handlers.sample or handlers.notFound variable names based on some logic (which is not mentioned here)
selectedHandler(data,function(status,payloadData){
// somelogic with the status and payloadData
});
And second parameter of this function which is a complete function in itself
function(status,payloadData){
// somelogic with the status and payloadData
}
is going as the second parameter in handlers.sample or handlers.notFound which is a Callback. So execution of Callback in the current context is execution of this function (this function is anonymous because it has no name and getting executed as Callback)

nodejs call two class functions synchronous

I have two class functions in nodejs and I want to call both synchronously both one after another.
var questionData = questionModelObj.getQuestionbyId(req.params.id);
var answerData = answerModelObj.getAnsByQuesId(req.params.id);
Here questionModelObj take time to execute and it calls answerModelObj without completing questionModelObj. It results answerData overwrites the questionData.
Please suggest how to call these two functions one after another(synchronous) and explain.
One option is to execute the second function as a callback to the first. This is pretty standard for Javascript.
// Declare variable to hold response
var answerData;
var questionData = questionModelObj.getQuestionbyId(req.params.id, function() {
// This is the callback function
answerData = answerModelObj.getAnsByQuesId(req.params.id);
}
The function signature of getQuestionbyId would then need to take in 2 parameters, the second being a function to be called later on:
questionModelObj.getQuestionbyId = function(id, callback) {
// Do things with the id
callback();
}
The callback function is then called after getQuestionbyId is done.

'This' is undefined in typescript even using arrow func in a promise

When I receive the 'catch' callback, 'this' is undefined, even using arrow funcitions. Any ideas?
private all(req: Request, res: Response): void {
EntityRepository.getRepositoty(req.params.etName).then(repo => {
...
}).catch((err) => {
this.handleError(res, err); // here I get undefined.
});
}
how the all func is called.
It's called based on a Express route.
Added mathod bind as #jfriend00 suggested.
constructor() {
// Connect to database.
DataAccess.connect();
// Starts configuring routes for api
this.router = Router();
// Bad request due to absence of entity type.
this.router.get("/", (req, res) => {
res.statusCode = 400;
res.statusMessage = SysMsgs.error.noEntityTypeSpecified.message;
res.send();
});
// Added method 'bind'.
this.router.get(this.routeBase, this.all.bind(this));
this.router.get(this.routeBase + "/:id", this.findOne.bind(this));
}
With arrow functions, this will retain the value it had in the scope prior to the arrow function call. In your case, that means it will have whatever value this was at the beginning of your all function. So, that value of this depends upon how the all function is called.
And, based on your this.router.get() where you specify this.all as the callback, that means that this inside of all will be set to whatever Express sets it to when it calls that callback. And, that is undefined.
You can fix your issue by using .bind().
this.router.get(this.routeBase, this.all.bind(this));
That will assure that the appropriate this is set when .all() runs. And, then your arrow function inside of all() will use that value of this.
Note: you will need to use .bind() for any method you are passing as a callback where you expect this to be the object inside the callback. When you pass something like this.all, the value of this is lost and only a reference to the method is passed. The caller then calls that method as a normal function with no object binding. You use .bind() to take control of that yourself. .bind() essentially creates a little stub function that reattaches the appropriate this pointer by calling your method using it.
You could also make your own wrapper arrow function for it:
this.router.get(this.routeBase, (req, res) => this.all(req, res));
which as Saravana points out will retain TypeScript type checking.
While #jfriend00's answer covers the problem with your code and there is nothing wrong with using bind in JavaScript, there is one little problem when you use bind in TypeScript though. The type signature for bind is:
bind(this: Function, thisArg: any, ...argArray: any[]): any;
Notice that bind returns any meaning any type checks on the returned function is turned off, which leads to subtle bugs. Take the following for example:
// Assume the definition for `router.get` is something similar to this:
get(routeBase: string, callback: (req: Request, resp: Response) => void)
// ... and in the place you are calling it
private all(param: number): void {
}
this.route.get(this.routeBase, this.all.bind(this));
// No compiler error, but `all` and the callback parameter expected by `route.get` don't match!
If you would like to keep type checking you can use something like below instead of bind:
this.route.get((req, resp) => this.all(req, resp));
See: https://github.com/Microsoft/TypeScript/issues/212

Understanding Callbacks

I am very confused when it comes to javascript callbacks... Especially when attempting to learn node at the same time.
You see, I understand the concept of passing a function as a parameter to another function but what confuses me mostly are the premade modules that "accept" callbacks functions.
For example a simple request looks like so:
var request = require('request');
var url = 'http://www.google.com';
request.get(url, function(error, response, body) {
if(error){
console.log('This request resulted in an error', error);
}
console.log(body);
});
This get method accepts a url and a callback.
Who defines that a callback is accepted? The developer of the specific method?
Also, the 3 parameters that we pass within the function (error, response, and body)... how does the request module know to populate these variables with data on return?
Imagine get implemented more or less like
function get(url, callback) {
var response = they somehow retrieve the response;
var body = they somehow parse the body;
var error = ...;
// they call you back using your callback
callback( error, response, body );
}
How these values are computed is irrelevant (this is their actual job) but then they just call you back assuming your function actually accepts three arguments in given order (if not you just get a runtime error).
In this sense they also kind of "accept" your callback by ... well, calling it back :) There is no worry about the number of arguments as Javascript is very "forgiving" - methods can be called with any number of arguments, including less or more arguments than the function expects.
Take this as an example:
// they provide 3 arguments
function get( callback ) {
callback( 0, 1, 2 );
}
// but you seem to expect none
get( function() {
});
The get assumes that the callback accepts three arguments while I pass a method that supposedly doesn't accept less arguments. This works, although the three arguments, 0, 1 and 2 are not bound to any formal callback arguments (you can't directly refer to them), they are there in the arguments object, an array-like structure:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
Take another example
function get( callback ) {
callback( 0, 1, 2 );
}
get( function(a,b,c,d,e) {
});
This time the callback I pass seems to expect more arguments than the caller provides and still, no problem here. Arguments are bound to consecutive call parameters, a will get 0, b will get 1, c will get 2 and d and e will get undefined inside the callback body.
Both examples show that the caller doesn't really care how your callback is defined, whether its number of arguments matches the expectation or not. In case of any mismatch, the worst what could happen is a runtime error:
function get( callback ) {
callback( 0, 1, 2 );
}
get( 1 );
// Uncaught TypeError: callback is not a function
The designer of a function/method decides what arguments it will accept and process. So, if a function accepts a callback as an argument, then it is the design and implementation of the function that decided that and will call the callback at the appropriate time.
It is also the implementer of the function/method that decides what arguments will be passed to the callback when it is called. What confuses many people is that is is the caller of the function that defines the callback function itself that will be called by the main function/method. But the arguments of the callback function MUST be declared to match what the main function/method will pass to the callback. The labels given to the arguments when you define the callback are merely labels for programming convenience (as a means of referring to the arguments by name). How the callback's arguments are defined in the callback function has nothing to do with what is actually passed to the callback - that is all decided by the function/method that calls the callback. So, when you use request.get(), the contract for using it is that it accepts a callback that should be declared to have the arguments (error, response, body) because that's what it will be called with. Now, it is OK to leave off arguments at the end if you aren't going to use them (they will still be there, but you just won't have a convenient name to refer to them by), but you can't leave off arguments before any that you intend to use because order is important.
Who defines that a callback is accepted? The developer of the specific
method?
Yes, the developer of the function/method decides if a callback is accepted.
Also, the 3 parameters that we pass within the function (error,
response, and body)... how does the request module know to populate
these variables with data on return?
The request.get() method is programmed to pass three arguments (error, response, body) to its callback and that's what it will pass. No matter how the callback itself is actually declared, request.get() will pass these three arguments in that order. So, what confuses many people is that the caller of the function/method creates the callback function itself and gets to define the arguments for it. Well, not really. The creator of the callback must create a callback that matches the contract the the function/method is expecting. If it doesn't match the contract, then the callback likely won't function properly as it will get confused about what it was passed. Really the contract here is that the first argument to the callback will be an error value and if that value is not truthy, then the next two arguments will be response and body. Your callback must match that contact in order to use those arguments correctly. Because Javascript is not strictly typed, it will not be a run-time error if you declare your callback incorrectly, but your access to those arguments will likely be wrong - causing things to work incorrectly.
For example, if you declared your callback to be:
function myCallback(response, body, err) { .... }
Then, what your callback sees as response would actually be the error value because request.get() put the error value in the first argument, no matter how the callback itself is declared.
Another ways of seeing this is to define a callback with no defined parameters and then access them entirely with the arguments object.
function myCallback() {
console.log(arguments[0]); // err value
console.log(arguments[1]); // response value
console.log(arguments[2]); // body value
}
The same is true of the return value in the callback. If you are using Array.prototye.map(), it expects a callback that is passed three arguments and returns a new array element. Even though you are implementing the callback, you must define a callback that is compatible with that contract.
This get method accepts a url and a callback. Who defines that a callback is accepted? The developer of the specific method?
Yes. The author(s) of request.get() defined it specifically to expect and make use a "callback" function.
You can define your own functions to accept callbacks as well, especially useful when they depend on other asynchronous functions – such as setTimeout() for a simple example.
function delay(value, callback) {
setTimeout(function () {
// this statement defines the arguments given to the callback
// it specifies that only one is given, which is the `value`
callback(value);
}, 1000);
}
delay('foo', function (value) { // named to match the argument `delay` provides
console.log(value);
});
Also, the 3 parameters that we pass within the function (error, response, and body)... how does the request module know to populate these variables with data on return?
The arguments given and their order are part of request's definition, same as delay() above defining that (value) would be the argument(s) for its callback.
When providing a callback, the parameters only give the arguments names for you own use. They have no effect on how the main function (request.get(), delay(), etc.) behaves.
Even without naming any parameters, request.get() is still passing the same 3 arguments in the same order to the callback:
request.get(url, function () { // no named parameters
var error = arguments[0]; // still the 1st argument at `0` index
var body = arguments[2]; // still the 3rd argument at `2` index
if(error){
console.log('This request resulted in an error', error);
}
console.log(body);
});

Resources