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.
Related
I have a major problem with profiling in javascript with anonymous functions, I have always many anonymous functions - most of them are callbacks - and It makes analyzing results of profiler very hard for me.
Finally I decided to use named functions for callbacks, like this:
var f = function(callback) {
// Do something ...
callback();
}
f(function named_function() {
console.log('Sample callback function!');
});
I want to know that will I have any problems after making this change in my codes?
And will this type of function definition and passing reserve the name (named_function) anywhere?
The name will only be available inside the scope of the named function expression.
But there is a problem in IE 8 and lower. It will leak out to the outer scope, and will actually create a different function object, so you should nullify it if that's a problem.
f(function named_function() {
console.log('Sample callback function!');
});
var named_function = null;
See this article for more information: Named function expressions demystified
Or you could create it like this to solve the IE issue.
f(function() {
return function named_function() {
console.log('Sample callback function!');
};
}());
But that's a little ugly.
If you pass anonymous functions like that, the name will exist inside the function itself.
It will not exist in any other scope.
var f = function(callback) {
// Do something ...
callback();
}
f(function named_function() {
console.log(named_function); // Logs the function
console.log('Sample callback function!');
});
console.log(named_function); // Error: named_function is undefined
You don't have to complicate things.
Just name the function when you declare it
var foo = function foo(){};
Defining a named callback in a scope will cause it to be visible only in that scope. I would therefore conclude that there shouldn't be any naming conflicts. For example, the following code runs as expected:
(function named_callback() { console.log("Callback 1"); })();
(function named_callback() { console.log("Callback 2"); })();
Actually, you're still creating an anonymous function expression and assigning it to a local scoped variable f. Go for
function f( callback ) {
callback();
}
f( named_function );
function named_function() {
console.log('Sample callback function!');
}
That way, you're even avoiding the named function expression memory leak in <= IE8, plus since you're no longer creating a function expression but a *function declaration, you can even access f within the body.
I want to do something like that:
callbackfunc=function(client, id, callback){
callbackfunc2({'id': id}, myfunc);
}
var myfunc = function(err,result){
callback(new Error("Ein Fehler"));
}
callbackfunc(client, 38373,function(err, data){
console.log("Here:"+data+" Error:"+err);
});
How can I make the callback available in myfunc? I always get callback is not a function. If I write it his way it works. And I don't understand why it doesn't work when I had a special function.
callbackfunc=function(client, id, callback){
callbackfunc2({'id': id}, function(err,result){
callback(new Error("Ein Fehler"));
});
callbackfunc(client, 38373,function(err, data){
console.log("Here:"+data+" Error:"+err);
});
If you want callback available in a separate function, then you have to either:
Pass it to myfunc as an argument.
or
Declare myfunc inside the scope where callback is available.
It works in this example:
callbackfunc=function(client, id, callback){
callbackfunc2({'id': id}, function(err,result){
callback(new Error("Ein Fehler"));
});
because you are calling callback from within the scope in which it is defined. Remember function arguments are only available within the block of the function in which they are defined. They are not available outside that function unless you pass them to some other scope as an argument or assign them to some other variable that is itself available in a different scope.
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);
});
The following code errors out on the first .then with:
/Users/danielbyrne/working/mite_npm/mite.js:48
.then(configMan.validateSettings(parsedData))
ReferenceError: parsedData is not defined
I don't understand why this code is failing.
The call:
parseConfigurationFile(path.join(__dirname,'file.config'))
.then(validateSettings(parsedData));
The functions it calls:
function parseConfigurationFile(fileName) {
var readFile = q.nfbind(fs.readFile);
readFile(fileName,"utf-8")
.then(function(data) {
var deferred = q.defer();
// Return the Config 'settings' in JSON
deferred.resolve(JSON.parse(data));
return deferred.promise;
});
}
function vaidateSettings (data) {...}
The only way this works is if I change the function validateSettings to an anonymous function and place it inline like this :
parseConfigurationFile(path.join(__dirname,'file.config'))
.then(function(parsedData){...});
Why can i not chain named functions in this manner?
Your validateSettings call should be like this:
parseConfigurationFile(path.join(__dirname,'file.config'))
.then(validateSettings);
The reason is that the validateSettings needs to be referenced as a function and the .then will invoke that function with the correct parameter. Doing it the way you are doing it, you receive a reference error because parsedData is not available at the time the function call is bound.
I found this code where it says that I can run some db queries asynchronously
var queries = [];
for (var i=0;i <1; i++) {
queries.push((function(j){
return function(callback) {
collection.find(
{value:"1"},
function(err_positive, result_positive) {
result_positive.count(function(err, count){
console.log("Total matches: " + count);
positives[j] = count;
callback();
});
}
);
}
})(i));
}
async.parallel(queries, function(){
// do the work with the results
}
I didn't get the part what is callback function how is it defined ? second in the queries.push, it is passing the function(j) what is j in this and what is this (i) for
queries.push((function(j){})(i));
I am totally confused how this code is working?
The loop is preparing an array of nearly-identical functions as tasks for async.parallel().
After the loop, given it only iterates once currently, queries would be similar to:
var queries = [
function (callback) {
collection.find(
// etc.
);
}
];
And, for each additional iteration, a new function (callback) { ... } would be added.
what is callback function how is it defined ?
callback is just a named argument for each of the functions. Its value will be defined by async.parallel() as another function which, when called, allows async to know when each of the tasks has completed.
second in the queries.push, it is passing the function(j) what is j in this and what is this (i) for
queries.push((function(j){})(i));
The (function(j){})(i) is an Immediately-Invoked Function Expression (IIFE) with j as a named argument, it's called immediately with i a passed argument, and returns a new function (callback) {} to be pushed onto queries:
queries.push(
(function (j) {
return function (callback) {
// etc.
};
})(i)
);
The purpose of the IIFE is to create a closure -- a lexical scope with local variables that is "stuck" to any functions also defined within the scope. This allows each function (callback) {} to have its own j with a single value of i (since i will continue to change value as the for loop continues).
Callback is one of the coolest features. Callback is just another normal function. You can actually pass a function itself as a parameter in another function.
Say function foo() does something. You may want to execute something else right after foo() is done executing. So, in order to achieve this, you define a function bar() and pass this function as a parameter to function foo()!
function foo(callback){
// do something
callback();
}
function bar(){
// do something else
}
foo(bar());
//here we can see that a function itself is being passed as a param to function foo.
For more understanding here's the right link.
And
queries.push((function(j){})(i));
in javascript, this is another way of calling a function.
function(){
// do something.
}();
You don't actually need to define a function name and it can be called directly without a name. You can even pass params to it.
function(a){
alert(a)'
}(10);