JavaScript style for optional callbacks

JavascriptCoding StyleCallback

Javascript Problem Overview


I have some functions which occasionally (not always) will receive a callback and run it. Is checking if the callback is defined/function a good style or is there a better way?

Example:

function save (callback){
   .....do stuff......
   if(typeof callback !== 'undefined'){
     callback();
   };
};

Javascript Solutions


Solution 1 - Javascript

I personally prefer

typeof callback === 'function' && callback();

The typeof command is dodgy however and should only be used for "undefined" and "function"

The problems with the typeof !== undefined is that the user might pass in a value that is defined and not a function

Solution 2 - Javascript

You can also do:

var noop = function(){}; // do nothing.

function save (callback){
   callback = callback || noop;
   .....do stuff......
};

It's specially useful if you happen to use the callback in a few places.

Additionally if you are using jQuery, you already have a function like that, it's called $.noop

Solution 3 - Javascript

Simply do

if (callback) callback();

I prefer to call the callback if supplied, no matter what type it is. Don't let it fail silently, so the implementor knows he passed in an incorrect argument and can fix it.

Solution 4 - Javascript

ECMAScript 6

// @param callback Default value is a noop fn.
const save = (callback = () => {}) => {
   callback(); // Executes callback when specified otherwise does nothing
};

Solution 5 - Javascript

Rather than make the callback optional, just assign a default and call it no matter what

const identity = x =>
  x

const save (..., callback = identity) {
  // ...
  return callback (...)
}

When used

save (...)              // callback has no effect
save (..., console.log) // console.log is used as callback

Such a style is called continuation-passing style. Here's a real example, combinations, that generates all possible combinations of an Array input

const identity = x =>
  x

const None =
  Symbol ()

const combinations = ([ x = None, ...rest ], callback = identity) =>
  x === None
    ? callback ([[]])
    : combinations
        ( rest
        , combs =>
            callback (combs .concat (combs .map (c => [ x, ...c ])))
        )

console.log (combinations (['A', 'B', 'C']))
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

Because combinations is defined in continuation-passing style, the above call is effectively the same

combinations (['A', 'B', 'C'], console.log)
// [ []
// , [ 'C' ]
// , [ 'B' ]
// , [ 'B', 'C' ]
// , [ 'A' ]
// , [ 'A', 'C' ]
// , [ 'A', 'B' ]
// , [ 'A', 'B', 'C' ]
// ]

We can also pass a custom continuation that does something else with the result

console.log (combinations (['A', 'B', 'C'], combs => combs.length))
// 8
// (8 total combinations)

Continuation-passing style can be used with surprisingly elegant results

const first = (x, y) =>
  x

const fibonacci = (n, callback = first) =>
  n === 0
    ? callback (0, 1)
    : fibonacci
        ( n - 1
        , (a, b) => callback (b, a + b)
        )
        
console.log (fibonacci (10)) // 55
// 55 is the 10th fibonacci number
// (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...)

Solution 6 - Javascript

I got so tired of seeing that same snippet over and over I wrote this:

  var cb = function(g) {
    if (g) {
      var args = Array.prototype.slice.call(arguments); 
      args.shift(); 
      g.apply(null, args); 
    }
  };

I've got hundred of functions doing things like

  cb(callback, { error : null }, [0, 3, 5], true);

or whatever...

I'm skeptical of the whole "make sure it's function" strategy. The only legitimate values are a function or falsy. If someone passes in a non-zero number or a non-empty string, what are you going to do? How does ignoring the problem solve it?

Solution 7 - Javascript

A valid function is based on the Function prototype, use:

if (callback instanceof Function)

to be sure the callback is a function

Solution 8 - Javascript

If the criteria for running the callback is that whether its defined or not, then you're fine. Also, I suggest to check if its really a function in addition.

Solution 9 - Javascript

I have sinced moved to coffee-script and found default arguments is a nice way to solve this problem

doSomething = (arg1, arg2, callback = ()->)->
    callback()

Solution 10 - Javascript

It can easilly be done with ArgueJS:

function save (){
  arguments = __({callback: [Function]})
.....do stuff......
  if(arguments.callback){
    callback();
  };
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionhenry.oswaldView Question on Stackoverflow
Solution 1 - JavascriptRaynosView Answer on Stackoverflow
Solution 2 - JavascriptPablo FernandezView Answer on Stackoverflow
Solution 3 - Javascriptninja123View Answer on Stackoverflow
Solution 4 - JavascriptLucioView Answer on Stackoverflow
Solution 5 - JavascriptMulanView Answer on Stackoverflow
Solution 6 - JavascriptMichael LortonView Answer on Stackoverflow
Solution 7 - JavascriptG. MooreView Answer on Stackoverflow
Solution 8 - JavascriptMrchiefView Answer on Stackoverflow
Solution 9 - Javascripthenry.oswaldView Answer on Stackoverflow
Solution 10 - JavascriptzVictorView Answer on Stackoverflow