I understand that callbacks are functions you pass as a parameter into another function, such as in the following simple example:
function operation(a,b, callback) {
return callback(a,b);
}
function add(a,b) {
return a+b;
}
function multiply(a,b) {
return a*b;
}
console.log(operation(5,4,add)); // 9
console.log(operation(5,4,multiply)); // 20
What confuses me greatly about callback functions is when they are used in chained function calls, such as the following:
// Angular example
$http.get(...).then(function(req,res) {
// some actions here
});
// JQuery example
$( "li" ).each(function( index ) {
// some actions here
});
In both examples, how are the parameters in the anonymous function populated? Does this in any way relate to the callback logic I gave in the operation function example I gave or is this some other concept entirely?
My best guess for the angular example is that the http promise returns an array object [req,res] and the function parameters are pulled from the array in sequential order.
What is of specific interest to me is how I could define my own chained function call in this style. How can I define something like:
myObject.performAction(function(param1, param2, param3) {
// do stuff
});
If someone could give an example like that, it would be amazingly instructive.
The parameters are passed to callback function by the calling code - same as in your example return callback(a,b);
var myObject = {
a: 1,
b: 2,
c: 3,
performAction: function(callback) {
callback(this.a, this.b, this.c);
}
};
myObject.performAction(function(param1, param2, param3) {
// do stuff
});
Based on Igor's answer, I came up with the following to mock the $http.get(...).then() syntax:
var myObject = {
transform: function (value) {
// Perform some logic based on the value parameter
var squared = value*value;
var cubic = value*value*value;
return {
a: squared,
b: cubic,
action: function(callback) {
callback(this.a, this.b);
}
}
}
};
myObject.transform(12).action(function(a,b) {
console.log(a+b); // 1872
});
The idea is that in the transform function, you perform some logic on the value parameter so that a and b are derived from some calculations instead of just being hardcoded values. That way the callback in action becomes a lot more meaningful.
This effectively abstracts the parameters a and b from the user in the anonymous function call in action. This is why these parameters have to be documented for the API call to myObject.transform.action.
Related
I know the orientation in Stack Overflow is ask what you want and does not demonstrate how you would like it to be done, but I really do not know how to ask it in a better way and I'm not a Javascript expert... So... Let's go!
Let's imagine that we have the following Javascript functions with their respective outputs...
function func_a(param_a, param_b, param_c) {
alert(param_a);
alert(param_b);
alert(param_c);
}
function func_b(func_to_run) {
// Will call "func_a" HERE!
func_to_run("a", "b", "c");
}
func_b(func_a);
// a
// b
// c
My questions are:
Is there a correct and/or better way to pass parameters to the function "func_a" when we pass it as argument of function "func_b"?
How can I pass parameters to the function "func_a" in the function "func_b" call? Below I'll give you a hypothetical example...
function func_a(param_a, param_b, param_c) {
alert(param_a);
alert(param_b);
alert(param_c);
}
function func_b(func_to_run) {
func_b_param = "z";
// Will call "func_a" HERE!
func_to_run(value_from_func_b_call, value_from_func_b_call, func_b_param);
}
// Third parameter deliberately empty!
func_b(func_a("a", "b", ));
// a
// b
// z
I do not know if I explained it clearly! If there is any doubt or suggestion of improvement please tell me!
Is this what you are looking for?
function func_a(param_a, param_b, param_c) {
alert(param_a);
alert(param_b);
alert(param_c);
}
function func_b(func_to_run, a, b) {
func_b_param = "z";
// Will call "func_a" HERE!
func_to_run(a, b, func_b_param);
}
func_b(func_a, a, b);
You can pass the parameters to func_b:
function func_a(param_a, param_b, param_c) {
alert(param_a);
alert(param_b);
alert(param_c);
}
function func_b(func_to_run, param1, param2) {
func_b_param = "z";
// Will call "func_a" HERE!
func_to_run(param1, param2, func_b_param);
}
// Third parameter deliberately empty!
func_b(func_a, "a", "b");
Although that's a quick and dirty answer, if we had a better idea of what you are trying to achieve there might be a better answer. Especially with the new ES6 syntax and the spread operator.
How can I pass parameters to the function "func_a" in the function
"func_b" call?
If interpret Question correctly you can pass an object to func_b as parameter where properties are set to 1) the function to call; 2) the parameters as an array passed to function to call; 3) the function to call within the function passed to call.
You can use Reflect.apply() to call the outer function to call, optionally setting this within the outer function; pass and array with first element being inner function to call at, for example, func_b, second element being parameters to call function with preceded by spread element.
At first parameter of outer function to call define a parameter representing the inner function to be called; at second parameter define rest parameters func_a representing the parameters passed to inner function to be called.
function func_a(fn, ...args) {
for (let arg of args) fn(arg)
}
function func_b(func_to_run) {
// Will call "func_a" HERE!
Reflect.apply(func_to_run.fn, null, [func_to_run.innerFn, ...func_to_run.args]);
}
func_b({
fn: func_a,
innerFn: alert,
args: ["a", "b", "c"]
});
Here are two callback function:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p)'
}
If I want use callback_a
function test(callback){
if(condition){
callback();
}
}
test(callback_a);
But the function test isn't applicable to callback_b, So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
There are three options:
The easiest way is to use spread operator:
function test(callback, ...callback_args) {
callback(...callback_args);
}
in this case the invocation of test for function callback_b would be like this:
test(callback_b,"b")
The second way is using arguments which are scoped to any function in JavaScript:
function test(callback) {
callback.apply(null, arguments.slice(1));
}
the invocation of test for function callback_b would be the same:
test(callback_b,"b")
Another options is to use partially applied functions. In this case you should define b_callback like this (ES6 syntax):
let callback_b = (p) => () => void{
alert('b says'+ p)'
}
or without ES6:
function callback_b(p) {
return function(){
alert('b says'+ p)'
}
}
and invoke it like this:
test(callback_b("b"))
There is a special object called arguments that gets created when a function is invoked. It's an array-like object that represents the arguments passed in to a function:
It can be used like this:
test();
// no arguments passed, but it still gets created:
// arguments.length = 0
// arguments >> []
test(a);
// ONE argument passed:
// arguments.length = 1
// arguments >> [a]
test(a,b,c,d);
// FOUR arguments passed:
// arguments.length = 4
// arguments >> [a,b,c,d]
Knowing this, one can call a callback with the rest of the arguments passed in from the parent function using apply like this:
function test(callback) {
callback.apply(null, Array.prototype.slice.call(arguments, 1));
}
// arguments passed into test are available in the function scope when
// .slice is used here to only pass the portion of the arguments
// array relevant to the callback (i.e. any arguments minus the
// first argument which is the callback itself.)
//
// N.B. The arguments object isn't an array but an array like object so
// .slice isn't available on it directly, hence .call was used here)
Might be worth reading up on:
The arguments object
Function.prototype.apply, Function.prototype.call and Function.prototype.bind as they are way to bind a context and arguments to a function (i.e. they'll work with the arguments object to call a function where you may not know how many arguments will be passed)
So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
Basically, you don't. The function receiving the callback is in charge of what the callback receives as arguments. When you call Array#forEach, it's Array#forEach that decides what arguments your callback gets. Similarly, String#replace defines what it will call its callback with.
Your job is to say what test will do, what it will call its callback with. Then it's the job of the person using test to write their callback appropriately. For instance: You might document test as calling the callback with no arguments. If the caller wants to use callback_b, then it's up to them to handle the fact that callback_b expects a parameter. There are several ways they can do that:
The could wrap it in another function:
test(function() {
callback_b("appropriate value here");
});
...or use Function#bind
test(callback_b.bind(null, "appropriate value here"));
...but it's their problem, not yours.
Side note: If they pass you callback_b and you call it without any arguments, you won't get an error. JavaScript allows you to call a function with fewer arguments than it expects, or more. How the function handles that is up to the author of the function.
You can pass an anonymous function as the callback that will itself return your desired callback function with parameters.
test(function() { return callback_b(' how are you'); });
see this working snippet that will first use callback_a, then callback_b (with parameter) as the callback:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p);
}
function test(callback){
if(true){
callback();
}
}
test(callback_a);
test(function() { return callback_b(' how are you'); });
You can pass the parameter while calling the callback
function test(callback){
if(condition){
callback();
}
else if(other condition){
callback("b");
}
}
test(callback_b);
You can write your callback function like
function callback_a_b(){
if(arguments.length){
var arg = [].slice.call(arguments);
alert('b says'+ arg[0])
}
else{
alert('a');
}
}
You can pass array of parameters as second param of test function or in ES6 use spread operator read more here
function test(callback, params){
if(condition){
if (params === undefined){
callback();
} else {
callback.apply(null, params); //params must be array
//ES6: callback(...params);
}
}
}
test(callback_a);
test(callback_b, [" whatever"]);
I've just checked in my browser (ffox 51.0.1) that the following works:
function test(callback,other_args){if(condition){callback(other_args);}}
results:
condition=true
test(callback_a)
=> shows the alert with 'a'
condition=false
test(callback_a)
=> doesn't show anything
condition=true
test(callback_b,"pepe")
=> shows the alert with 'b sayspepe'
condition=false
test(callback_b,"pepe")
=> doesn't show anything
I am writing a javascript library to abstract ajax requests to my HTTP API.
Each of my javascript functions is a wrapper for jquery's ajax call, which makes a callback to the user on completion.
Eg.
mylib.doThing( "foo", { success:function(){alert("done");});
In the case where I want to execute mylib.doFoo twice in series, I have something like:
mylib.doThing( "foo", { success:function(){ mylib.doThing( "bar", { success:function(){alert("done");}); });
For anything more that two steps, this gets messy very quickly.
Is it possible to provide a cleaner syntax, perhaps more like the following? And how would I need to implement mylib.doThing()?
mylib.doThing("foo").mylib.doThing("bar").alert("done");
function doThingFluent(a, b) {
return {
_name : a,
_chainedCall : b,
doMoreThingFluent : function(a1) {
return doThing(a1, this);
},
done : function(callback) {
var chained = this._chainedCall;
var name = this._name;
while (chained) {
callback = function(n, c) {
return function() {
mylib.doThing(n, { success : c });
};
} (name, callback);
name = chained._name;
chained = chained._chainedCall;
}
mylib.doThing(name, {success: callback});
}
};
doThingFluent("foo").doMoreThingFluent("bar").done(function(){alert("done");})
If you just want to chain unspecified number of successful requests with doing something in the end, listing all things to do as a natural list instead of chain of methods would be even cleaner:
mylib.doThing("foo", "bar", /* all done callback -> */ function() { alert("done") })
doThing would have inside a factory that would create either nested callbacks or - even better - iterative manager that'd run all requests in sequence and then call final callback.
I'm wondering if there is a way to implement a generic "memoize" functional (as in a function with a function as input and a function as output, as python's decorators) capable of handling also cps-style functions.
for a normal function (as in "the result value comes back by the return, the parameters are only for input!") a memoize function can be as simple as (in javascript)
function memoize(fun) {
var cache = {};
return function () {
var args = Array.prototype.slice.call(arguments);
if (args in cache)
return cache[args];
var ret = fun.apply(this, arguments);
cache[args] = ret;
return ret;
};
}
but a cps-style function cannot be memoized by my simple memoize function, cause I need to evaluate "again" the arguments of type function, knowing also the parameter to pass to them.
For example, given the function
function cps(param, next) {
var ret = param + 1;
// setTimeout for simulate async behaviour
setTimeout(function () {
next(ret);
}, 0);
}
maybe I can find that next is a function, but its signature (well... maybe, but it's tricky), and definitely not the parameters used in the function!
Can someone tell me I'm wrong? :D
I'm interested to be able to memoize an half dozen of cps-style functions and I don't want to mess with the logic inserting a "cache" in every one of them.
I'm new to CPS, but I think you'll have to construct your functions in a particular way.
Your CPS functions have the following structure (generalising from your example):
function cps(param, next) {
var ret = someFunctionOfParam(param);
// setTimeout for simulate async behaviour
setTimeout(function () {
next(ret);
}, 0);
}
So, you could use your standard memoizer, and construct the CPS function as well. Keeping this separate for the sake of it, first the CPS-maker (assumes the last argument for the functions is always the function to pass to):
function cpsMaker(transformFunc) {
return function() {
var args = Array.prototype.slice.call(arguments);
var next = args.pop(); // assume final arg is function to call
var ret = transformFunc.apply(this,args);
// setTimeout for simulate async behaviour
setTimeout(function () {
next(ret);
}, 0);
}
}
And then the memoizer can be used in conjunction with it:
function plusOne(val) {
return val+1;
}
var memoPlusOne = memoize(plusOne);
var cpsMemPlusOne = cpsMaker(memoPlusOne);
cpsMemPlusOne(3,function(n){console.log(n)});
The point is to separate the memoization of the transform from the CPS construction.
Thank you for introducing the idea of memoized CPS; even if this answer is rubbish, it has been an eye-opener for me!
Assume I have a js function. From some other point in the program, I want to run its code, but not its return statement. In its place, I would like to run some other return statement that references the variables in the scope of the original function.
Is there a way to do this, other than loading up the function source, replacing the return, and using eval on the result? Minimal modification of the original is possible, though it should not affect the original's performance by adding e.g. an extra function call.
You could try something like this, but I'm not sure it meets your conditions.
Edit: Fixed to work in jsfiddle
// Modified to set all "shared" variables as "members" of the function.
var test = function() {
test.val = "one";
test.val2 = "two";
return 1;
}
// Using different result
function test2() {
test();
return test.val2;
}
Unless you're able to restructure your methods to accommodate a callback or introduce some other parameter-based logic-flow (not an option for 3rd party code), you're out of luck.
Here's a callback sample (fiddle, credit to dzejkej's answer)
function foo(callback) {
var x = 2;
// pass your values into the callback
return callback ? callback.call(this, x) : x * 2;
}
document.write(foo());
document.write("<hr/>");
// specify the parameters for your callback
document.write(foo(function(x){ return x * 4;}) );
You can introduce a callback function that will get called if available otherwise "standard" value will be returned.
function test(callback) {
// ...
return callback ? callback.call(this) : /* original value returned */ "xyz";
}
test(function() { /* "this" is same as in test() */ });
EDIT:
If you want to pass variables inside callback then you just list them in the .call() function.
Example:
function test(callback) {
var a = 4;
var b = 2;
// ...
return callback ? callback.call(this, a, b) : a * b;
}
test(); // 8
test(function(a, b) { return a + b; }); // 6
See this fiddle.
Provided that you would keep variables of the outer scope function within a single object, you could try something like the following:
function original(a, b, c, rep) {
var data = {};
// Do some fancy stuff but make sure to keep everything under data
data.a = a.replace(/foo/, 'bar');
...
if ( Object.prototype.toString.call(rep) === '[object Function]' )
return rep.call(data);
return data;
}
function replacement() {
return 'foo' + this.a;
}
// Now let's make use of both the original and the replacement ...
console.log(original('foo', x, y)); // => {a: "bar", b: ...}
console.log(original('foo', x, y, replacement)); // => {a: "foobar", b: ...}
Hope, it's what you where asking for.
cheers
I think you really misunderstand the concept of return statement. The return statement of a function will simply return a value, or an object, or undefined if there is no return parameter specified.
If all you're trying to do is execute a function but "not its return statement" than you would just invoke the function and not do anything with the returned value/object:
However, if what you mean is that you would like to execute a function but not execute the "parameter" to its return statement then that literally means to selectively execute an arbitrary portion of the body of a function. And as far as I know that is not possible (without using reflection to get the function definition, modify the definition, and then dynamically invoking the modified version - which you said you didn't want to do).