2

How to create a utility function in JavaScript like below, where URL is the first parameter and success and error callbacks will be last parameters(mandatory) with any number of optional parameters in-between

function performAction(URL, optional1, optional2, successCallback, errorCallback){
      //api call with given url and other parameters with callback
}

and the function should be called like below without destructuring

performAction(url, op1, success, error);
Akhilesh krishnan
  • 799
  • 2
  • 8
  • 22
  • 3
    You should use promises. – SLaks Feb 22 '18 at 17:41
  • `successCallback = successCallback || optional1 || optional`. Or just dont do this at all – Jonas Wilms Feb 22 '18 at 17:43
  • 2
    I'm not sure why you want to do this (maybe explain more) but it seems like bad design. You can also contain your optional parameters in an object called optionals (which has all the other options) – Emmanuel N K Feb 22 '18 at 17:44
  • @EmmanuelNK im trying a design as done mongoose functions where the callback is always at last with optional parameters in between like `Model.update(query, { name: 'jason bourne' }, options, callback)` where the options parameter is not mandatory – Akhilesh krishnan Feb 22 '18 at 17:49
  • 1
    But here `options` is just _one_ parameter, an object. `if (typeof callback === "undefined" && typeof options === "function") { callback = options }` – Andreas Feb 22 '18 at 17:52
  • Perfect @Andreas so what if i have to have few more? Please point out if there is some design flaw in detail – Akhilesh krishnan Feb 22 '18 at 17:53
  • @Akhileshkrishnan see this: https://stackoverflow.com/questions/10855908/how-to-overload-functions-in-javascript/10855939 – Emmanuel N K Feb 22 '18 at 17:57
  • The [solution](https://stackoverflow.com/questions/48933942/how-to-have-optional-parameters-in-between-mandatory-parameters-in-javascript-fu?noredirect=1#comment84874648_48933942) is already in the comments – Andreas Feb 22 '18 at 17:58

2 Answers2

1

Use rest parameters as follows :

   function varArgs (URL, ...options) {
     // Check that options has at least two values for the callbacks
     if (options.length < 2)
       throw (new Error ('varArgs : no callbacks specified'));

     // Extract last two values in options and assign to callbacks
     let cb_error = options.pop (),
         cb_success = options.pop ();

     // Verify that callbacks are indeed functions
     if (typeof cb_error != 'function' || typeof cb_success != 'function')
       throw (new Error ('varArgs : callbacks not functions!!'));

     // now process URL using options and call appropriate callback.

     // ....
}   
HBP
  • 15,685
  • 6
  • 28
  • 34
  • 1
    Ya, this worked for me, I'm having the optional arguments inside an object so that I could have many of them as 'key: value' `function Get(URL, ...options) { //code as per the given answer and ... if(options.length == 3){ opt1 = options[0]['optional1']; opt2 = options[0]['optional2']; }}` – Akhilesh krishnan Feb 23 '18 at 08:51
0

There's really two ways to do what you are describing, but neither of them 100% meets your requirements . . .

1) Use a required options array or object to hold optional parameters

In this approach, you would have a parameter "on call" to hold 0 or more parameters in addition to the required ones:

function performAction(URL, options, successCallback, errorCallback) {...}

This would support your desired functionality, but would force the user to provide, at a minimum, an empty Array or Object as the second parameter:

performAction(someURL, [], function1, function2);
performAction(someURL, [foo, bar], function1, function2);
performAction(someURL, {}, function1, function2);
performAction(someURL, {foo: "abc", bar: "xyz"}, function1, function2);

2) Use the newer "rest parameters" provided by ES6

This approach would allow you to make any additional parameters truly optional (no empty value needed), however, rest parameters are required to be the last parameter in the function:

function performAction(URL, successCallback, errorCallback, ...options) {...}

So, while you would not get the order of parameters that want, it would meet your desire to have the optional parameters be truly optional:

performAction(someURL, function1, function2);
performAction(someURL, function1, function2, foo);
performAction(someURL, function1, function2, foo, bar, baz);

Any additional parameters will be pulled together and stored in an array, that can be accessed within the function. See more on rest parameters here on MDN.

The other drawback to this approach is that ES6 is still being rolled out into browsers, so it is not supported in browsers just yet. You can use a transpiler like Babel to make it understandable to those browsers, but that does add some complexity to this approach.


UPDATE: Technically, there is also a 3rd option to work with the arguments object that is built into JavaScript functions (more info here on MDN), but this would be messy. You would need to define the function with the three required parameters:

function performAction(URL, successCallback, errorCallback) {...}

. . . but then call the function with (potentially) more than the three expected arguments:

performAction(someURL, function1, function2);
performAction(someURL, foo, function1, function2);
performAction(someURL, foo, bar, baz, function1, function2);

The function would then need to access the parameters in a complicated way . . . you'd actually need to redefine the last two parameter values, based on their placement in the arguments object:

var argLength = arguments.length;
successCallback = arguments[argLength - 2];
errorCallback= arguments[argLength - 1];

To access any of the remaining values, you would either need to:

  • iterate through them using a starting index of 1 and running through them while the index was less than argLength - 2, or
  • convert the arguments object into an array and slice the values out into their own array

NOTE: I do NOT recommend this approach, but, technically, it is possible.

talemyn
  • 7,822
  • 4
  • 31
  • 52