Passing a parameter to a callback function [duplicate] - javascript

This question already has answers here:
JavaScript: Passing parameters to a callback function
(16 answers)
Closed 7 years ago.
I needed to pass a parameter to a callback function in Javascript, so I did the following which creates an anonymous function as a string and then passes it:
var f = "var r = function(result) {do_render(" + i + ",result.name,result.data);}"
eval(f)
$.getJSON("analysis?file=" + getParameterByName('file') + "&step=" + i,r);
This doesn't seem like a great idea however. Is there a better way?

There's several techniques that you can use to do this. One of which is to create a new function which "seals off" one of the variables:
function myCallback(i, result) { ... }
function createCurriedFunction(i, func, context) {
return function (result) { func.call(context, i, result); }
}
for (i = 0; i < 5; i += 1) {
var curriedFunc = createCurriedFuncion(i, myCallback, this);
$.getJSON(url, curriedFunc);
}
Context is the object for which the "this" will refer to in the callback function. This may or may not be needed for what you're doing; if not you can just pass in null.
There's actually a function that does exactly that called bind, and is used like
var curriedFunc = myCallback.bind(this, i), which will seal off the first variable.

It looks like you are having issues closing over i and to solve it used eval. Instead of that, simply close over it using an immediately invoked function expression (IIFE) like this:
(function(i){
//create a closure of the value of i
//so that it takes the immediate value of it instead of the end value
//based on the assumption i is from a loop iterator
$.getJSON("analysis?file=" + getParameterByName('file') + "&step=" + i,
function(result){
do_render(i, result.name, result.data);
}
);
})(i);//pass i into the IIFE in order to save its immediate value

You can simply do
var url = "myurl";
$.getJSON(url, function(result){
//callback function
});

Related

What does the expression eval(function(){ } ()) do in javascript?

I found this script on a web page:
eval(function(a, b, c, d, e, f) {
//do some thing here and return value
}(val1, val2, val3, val4, val5, {}))
Do you know what above expression really does?
Let's unpack things one by one, first the inner part
function(a, b, c, d, e, f) {
//do some thing here and return value
}(val1, val2, val3, val4, val5, {})
In fact, let's simplify it even more
(function() { //takes no paremeters
console.log("this was executed");
}())//no parameters given here
This is called an immediately invoked function expression or very often shortened to IIFE. It is rather boring, honestly, the name is the entire description but let me rephrase - it's a function that is declared and then executed straight away. This is done by the final () brackets - exactly how you'd otherwise execute a function, for example parseInt(). In this simple case, it simply prints something to the console and finishes.
However, it is a normal function and it can do anything you want, including take parameters:
(function(param1, param2) { //takes two parameters
console.log("this was executed with parameters", param1, param2);
}("hello", "world"))//these are passed to the function
So, you can do a lot more stuff. You can even return a value and the parameters passed to the function could be variables:
var someVariable = "hello";
var anotherVariable = "world";
var resultFromIIFE = (function(param1, param2) { //takes two parameters
console.log("this was executed with parameters", param1, param2);
return param1 + param2;
}(someVariable, anotherVariable));
console.log(resultFromIIFE);
So, hopefully that clears up the inner part. I'll get back to it.
As for eval - it takes a string and executes it as JavaScript code.
eval("console.log('this comes from eval()')");
var evalCanReturnResults = eval("1 + 2");
console.log(evalCanReturnResults);
var a = 3;
var b = 4;
var evalCanUseVariables = eval("a + b");
console.log(evalCanUseVariables);
That's a quick crash course in eval. It's enough to understand that it can take any arbitrary string and execute it as JS.
So, if we put the two together, the inner IIFE will most likely be generating some code dynamically for eval to execute. For example, you could have something like::
eval(function(limit, toPrint) {
return "for (var i = 0; i < " + limit + "; i++) { console.log('" + toPrint + "') }"
}(3, "foo"))
Which will generate a for loop with the parameters specified and execute it.
This does 2 things.
Creates an anonymous function and executes it.
eval the results of the function.
Here is a simplified example.
eval(
function(a) {
let str = a + ' world'
let toEval = 'console.log("' + str + '")'
// the string is now -> 'console.log("hello world")'
return toEval
}('hello')
)
Defines a function that accepts a string, adds ' world', and creates code as a new string, then returns it. This function has no name, and so is anonymous.
Executes the function with param hello.
Then execute the returned code (in string form) using eval.
eval method is actually used for executing scripts as soon as that eval statement is executed. It can execute script in string format. Just like below -
eval("console.log('hello world')")
In your case, it is actually nothing but just executing the script as soon as that statement is executed.

Invoke a JavaScript function with parameter in a function

Today, I saw the following code below:
log_execution_time = require('./utils').log_execution_time;
var fib = function fib(n) {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
};
var timed_fib = log_execution_time(fib);
timed_fib(5);
>>> Execution time: 1.166ms
I am curious about function log_execution_time. I don't know how it is.
You can see the input of log_execution_time is a function. How can it call the function with parameter? But all of the methods from w3school need a parameter when calling a function. I assume:
var log_execution_time = function (input_function){
console.time("Execution time");
// input_function
console.timeEnd("Execution time");
}
Thanks and regards
I think the OP is specifically about how the 5 parameter gets passed to the function input_function
Functions are first class objects in JavaScript. You can set identifiers and pass their references around just the same as any other object.
log_execution_time(fib); does not invoke fib, it passes a reference to fib into the log_execution_time function as the first argument. This means the internals can reference fib
timed_fib is a function which can reference the closure from that invocation of log_execution_time due to when it was created, so it can hence invoke the reference to fib as desired
Here is a simple example;
function log(msg) {
console.log(msg);
}
function wrap(fn) {
return function () { // some anonymous function to be our wrapper
console.log('Wrapped:');
fn.apply(this, arguments); // this line invokes `fn` with whatever arguments
// that were passed into the anonymous function
};
}
var foo = wrap(log);
foo('Hello World'); // logs
// Wrapped:
// Hello World
We could also have used the more usual way to invoke fn, for example fn("fizz buzz");, instead of .apply but that would mean we needed to know more about how to invoke fn, which could have been anything
Useful stuff:
Function.prototype.apply
Function.prototype.call
This is known as function currying, in this case the function is being curried with a parameter that also happens to be a function. It may look something like this:
function logTime(f) {
return function() {
var s = new Date();
var r = f.apply(null, arguments);
var e = new Date();
console.log('Time taken ' + (e-s));
return r;
}
}
function numberlogger(n) {
console.log("logged number: " + n)
};
var timedlogger = logTime(numberlogger);
console.log(timedlogger(2));
We call logTime, passing in numberlogger as an argument. Functions in JavaScript are objects and can be passed around like anything else. The logTime function returns a different function that is then stored in timedlogger. When we invoke timedlogger, we're actually invoking the function that logTime returned. That uses a couple of variables to keep track of the start and end times for timing, but uses apply (which every function in js has) to call the original function (numberlogger) whilst passing in any arguments supplied.
I suggest reading up on Functions in Javascript. Here's a nice article from the Mozilla Developer Network (MDN) which is in my opinion, a much better resource than w3schools
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
To answer your question though, functions in javascript are first class citizens, and what that means is that you can think of them as any other object (string,boolean,number etc). They can be saved in variables and they can be passed as arguments into other functions.
In your example, log_execution_time will actually return a function which is essentially a wrapper around the fib function that gets passed to it
The code can be like this:
var log_execution_time = function (input_function){
var f=function(args)
{
var t1=new Date().getTime();
input_function(arguments);
console.warn("Execution time:" +(new Date().getTime()-t1).toString());
}
return f;
}

How are arguments passed to anonymous callback functions?

I am learning AngularJS and have copied code this basic code from a tutorial (simplified/pseudo code to only include the parts relevant to this question).
The code works for me, but I am trying to better understand how the argument is being passed the the callback in the success method.
// jobService object
var jobService = {
get : function() {
return $http.get( 'some/api/url' );
}
};
jobService.get().success(function (data) {
$scope.jobs = data;
});
My question is, knowing that "normally" arguments are specifically passed into the functions when invoked i.e.:
function foo(arg1) {
alert(arg1); //alerts Hello!
};
foo('hello!');
How is the data argument being passed into the anonymous callback function?
is it:
Being "injected" by AngularJS?
Does the javascript engine simply use variables on the local scope called data?
does the javascript engine look for a data property on the parent object if the success method?
TL;DR
We're just defining that anonymous function, not calling it!
Thus data is a function parameter, not a function argument.
Long version
Let's take this into little pieces.
success() is a function. It's chain-called after jobService.get(). So, whatever the jobService.get() call returns, we're calling the success function of that object (say returnedObject.success()).
Back to success() itself. It can easily read other properties of its object (returnObject from the example above). Since we're passing in the anonymous callback function as an argument, success can easily do something like (narrowing it down to basic JS):
function success(callback) {
var whatever = "I'm passing this to the callback function";
callback(whatever);
}
which would actually call our anonymous function we passed in, and assign it whatever as data (don't forget we're just defining that anonymous function, not calling it!) . This makes data a function parameter, and it is basically a custom name that you use to represent and access what the success function passes into its callback function. You can use whatever you want there - this would still work perfectly fine:
jobService.get().success(function (somethingElse) {
$scope.jobs = somethingElse;
});
Hope I didn't make this too complicated. I was trying to explain it step-by-step from the plain JS standpoint because you can easily read Angular's source to see what it does, so I thought you needed this simpler explanation.
Here's a basic example replicating what's going on there (inspect the JS source, see how the output is the same in all three cases):
var debug = document.getElementById('debug');
function success(callback) {
var whatever = 'hello world';
debug.innerHTML += '<br>success function called, setting parameter to <span>' + whatever + '</span><br>';
callback(whatever);
}
function callbackFunction(someParameter) {
debug.innerHTML += '<br>callbackFunction called with parameter <span>' + someParameter + '</span><br>';
}
success(callbackFunction);
// anon function
success(function(val) {
debug.innerHTML += '<br>anonymous callback function called with parameter <span>' + val + '</span><br>';
})
// anon function 2
success(function(anotherVal) {
debug.innerHTML += '<br>second anonymous callback function called with parameter <span>' + anotherVal + '</span><br>';
})
span {
color: green;
}
<div id="debug"></div>
An example using an object, similar to what is done in your original code:
var debug = document.getElementById('debug');
var myObject = {
whatever: 'hello world',
success: function(callback) {
debug.innerHTML += '<br>success function called, fetching object property and setting the parameter to <span>' + this.whatever + '</span><br>';
callback(this.whatever);
},
modifyMe: function() {
debug.innerHTML += '<br>object property modified<br>';
this.whatever = 'another world';
return this; // this is crucial for chaining
}
}
// anon function callback
myObject.success(function(val) {
debug.innerHTML += '<br>anonymous callback function called with parameter <span>' + val + '</span><br>';
})
debug.innerHTML += '<br><hr>';
// chaining - calling a success function on a modified object
myObject.modifyMe().success(function(val) {
debug.innerHTML += '<br>anonymous callback function called with modified parameter <span>' + val + '</span><br>';
})
span {
color: green;
}
<div id="debug"></div>
Here's the relevant part in the source code:
promise.success = function(fn) {
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
Read more on GITHUB.

Correct way to pass a variable argument in a callback in JavaScript?

I feel this should be answered somewhere in the internet but I failed to find it, maybe because I'm not searching the correct terms but this is the problem: I have the following function:
function ParentFunction (DataBase, Parameters) {
for (k = 0; k < DataBase.length; k++){
var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
$.ajax({
url: CalendarURL,
dataType: 'json',
timeout: 3000,
success: function( data ) { succesFunction(data, k, Parameters);},
error: function( data ) { errorFunction ("Error",Parameters); }
});
}
}
I was getting errors in succesFunction(data, k, Parameters) because 'k' was always evaluated with the latest value. What is happening is that, when the for loop runs k is correctly increased but, when the callback function successFunction was executed, typically several ms after the loop was finished, it was always been evaluated with the last value of k, not the value of the loop the $.ajax was called.
I fixed this by creating another function that contains the ajax call. It looks like this:
function ParentFunction (DataBase, Parameters) {
for (k = 0; k < DataBase.length; k++){
var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
AjaxCall(CalendarURL, k, Parameters);
}
}
function AjaxCall(URL, GroupIndex, Parameters) {
$.ajax({
url: URL,
dataType: 'json',
timeout: 3000,
success: function( data ) { succesFunction(data, GroupIndex, Parameters);},
error: function( data ) { errorFunction ("Error",Parameters); }
});
}
and it works. I think when the function is called in the parentFunction a copy of the value of the arguments is created and when the callback executes sees this value instead of the variable k which by the time would have a wrong value.
So my question is, is this the way to implement this behaviour? Or is there more appropriate way to do it? I worry that either, different browsers will act differently and make my solution work in some situations and not work in others.
You are hitting a common problem with javascript: var variables are function-scoped, not block-scoped. I'm going to use a simpler example, that reproduces the same problem:
for(var i = 0; i < 5; i++) {
setTimeout(function() { alert(i) }, 100 * i);
}
Intuitively, you would get alerts of 0 through 4, but in reality you get 5 of 5, because the i variable is shared by the whole function, instead of just the for block.
A possible solution is to make the for block a function instead:
for(var i = 0; i < 5; i++) {
(function(local_i) {
setTimeout(function() { alert(local_i); }, 100 * i);
})(i);
}
Not the prettiest or easier to read, though. Other solution is to create a separate function entirely:
for(var i = 0; i < 5; i++) {
scheduleAlert(i);
}
function scheduleAlert(i) {
setTimeout(function() { alert(i); }, 100 * i);
}
In the (hopefully near) future, when browsers start supporting ES6, we're going to be able to use let instead of var, which has the block-scoped semantics and won't lead to this kind of confusion.
Another option – rather than creating a new named function – would be to use a partial application.
Simply put, a partial application is a function that accepts a function that takes n arguments, and m arguments that should be partially applied, and returns a function that takes (n - m) arguments.
A simple implementation of a left-side partial application would be something like this:
var partial = (function() {
var slice = Array.prototype.slice;
return function(fn) {
var args = slice.call(arguments,1);
return function() {
return fn.apply(this, args.concat(slice.call(arguments)));
}
}
}).call();
With this, then you can take a function that requires two arguments like:
function add(a,b) { return a + b; }
Into a function that requires only one argument:
var increment = partial(add, 1);
increment(1); // 2
increment(10); // 11
Or even a function that requires no arugments:
var return10 = partial(add, 5, 5);
return10(); // 10
This is a simple left-side only partial application function, however underscore.js provides a version that can partially apply an argument anywhere in the argument list.
For your example, instead of calling AjaxCall() to create a stable variable scope, you could instead do:
function ParentFunction (DataBase, Parameters) {
for (k = 0; k < DataBase.length; k++){
var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
var onSuccess = _.partial(succesFunction, _, k, Parameters);
$.ajax({
url: CalendarURL,
dataType: 'json',
timeout: 3000,
success: onSuccess,
error: function( data ) { errorFunction ("Error",Parameters); }
});
}
}
Here, we are using _.partial() to transform a function with a signature of:
function(data, index, params) { /* work */ }
into a signature of:
function(data) { /* work */ }
Which is the signature that the success callback will actually be invoked with.
Though admittedly, this is all pretty much just syntactical sugar for the same underlying concepts already described, it can sometimes conceptually help to think about problems like these from as functional perspective than procedural one.
This has to do with closures in javascript. Your anonymous functions each reference a variable outside of their current scope, so each function's "k" is bound to the original looping variable "k." Since these functions are called some time after, each function looks back to see that "k" is sitting at its last value.
The most common way to get around this is exactly what you did. Instead of using "k" in a nested function definition (which forces a closure), you pass it as an argument to an external function, where no closure is needed.
Here are a few posts with similar issues:
How do JavaScript closures work?
JavaScript closure inside loops – simple practical example
Javascript infamous Loop issue?

Use same variable that is defined in another function

This may seem simple to some but I am less experienced with JavaScript. I have two functions. One is called when my upload begins. The next is called when my upload ends.
In the first function, a variable is created, a unique id used for the upload. What is the best way to go about reusing it in my second function since it is not global? The reason I defined it within my function is because every time a user clicks the upload button, the function is called and a NEW id is created for that upload, that is why I do not want to define it outside because then the same id would be served for a second upload unless the page is refreshed.
Anyone got any suggestions?
function uploadstart() {
function makeid() {
var text = "";
var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 32; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
var rand_id = makeid();
}
uploadfinish(){
//rand_id will be undefined
}
Pass in that var as a parameter
uploadstart(){
function makeid()
{
var text = "";
var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 32; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
var rand_id=makeid();
//Pass in rad_id
uploadfinish(rand_id);
}
uploadfinish(radomID){
//rand_id will be undefined
}
Try declaring rand_id in global scope (before everything)
var rand_id;
function bla....
The solution to this problem depends on how and where you use those two functions. If you pass them as callbacks to another function from some ajax library, or something like that, and if you control that library call, you could use a closure.
So, if for example you do something like this when an upload is begun:
Library.foo(..., uploadstart, uploadfinish);
You could define makeID as a global function, and then bind the generated id to your callbacks using a function like this:
function bind_id(rand_id, my_function) {
return function() { // return a closure
return my_function(); // my_function is executed in a context where rand_id is defined
}
}
Then you define your callbacks using rand_id as if it were global (actually, it will defined in the closure):
function uploadstart() {
// use rand_id as you wish
}
function uploadend() {
// use rand_id as you wish
}
When you need to call your Library.foo function, first generate the rand_id, then bind it to the start and end callbacks:
var new_rand_id = randID();
Library.foo(..., bind_id(new_rand_id,uploadstart), bind_id(new_rand_id,uploadend));
This way you'll pass to foo not the original uploadstart and uploadend, but two closures where rand_id is defined and i the same for both, so that callback code can use that variable.
PS: closures are one of the most powerful and trickiest features of javascript. If you're serious about the language, take your time to study them well.
What do you do with the rand_id once you've created it? Could you not just call uploadfinish with the rand_id as a parameter?
function makeid()
{
...
}
var rand_id=makeid();
uploadfinish(rand_id);
}
uploadfinish(id){
//rand_id will be 'id'
}
[EDIT] Since you said you need to call the function externally, check out this page for details about callbacks:Create custom callbacks
function doSomething(callback) {
// ...
// Call the callback
callback('stuff', 'goes', 'here');
}
function foo(a, b, c) {
// I'm the callback
alert(a + " " + b + " " + c);
}
doSomething(foo);
That will call doSomething, which will call foo, which will alert
"stuff goes here".
Note that it's very important to pass the function reference doSomething(foo),
rather than calling the function and passing its result like this: doSomething(foo()).

Categories

Resources