qslcna
qslcna

Reputation: 129

How to pass a callback function with multiple possible parameter lists

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.

Upvotes: 2

Views: 4245

Answers (7)

JeB
JeB

Reputation: 12143

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"))
    

Upvotes: 7

Pineda
Pineda

Reputation: 7593

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:

Upvotes: 2

malarres
malarres

Reputation: 2946

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

Upvotes: 0

barbsan
barbsan

Reputation: 3468

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"]);

Upvotes: 0

ricky
ricky

Reputation: 1684

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');  
  }
  
}

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1075895

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.

Upvotes: 2

Sarah Groß
Sarah Groß

Reputation: 10879

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'); });

Upvotes: 0

Related Questions