Pawel Zadrozny

@pawelzny

About confusing or poorly documented features of tools and libraries I use.


Create JavaScript callback with no pain

10 Jul 2016 development | #javascript #npm #nodejs

Creating function which accepts another function as last parameter is not challenging, but boring and tiring. Let’s change it!

Standard callback implementation

Designing custom function which accepts callback, you need to take care of few things:

  • Declare last argument as callback function
  • Check if this argument is callable
  • Encapsulate invocation in try-catch block
  • Pass parameters to callback function

In result you will get something like that:

Create myFunc which accept callback

function myFunc (param1, param2, callback) {
  let error = null;

  /* some logic */

  // check if callback is callable
  if (typeof callback === 'function') {
    // make environment safe against bugs
    try {
      // if your logic returns error pass the error
      if (error) {
        callback(error);
      } else {
        // else pass other arguments
        callback(null, param1, param2);
      }
    } catch (err) { /* pass, callback bugs */ }
  }
  /* rest of your logic */
}

Invoke myFunc with callback

myFunc('test', true, function(err, is_test, flag) {
  if (err) {
    /* error logic */
  } else {
    /* other logic */
  }
});

Your callback is tightly coupled with exactly third argument in example above. It is acceptable but not so flexible. Although whole boilerplate is not hard to understand, writing it again and again is not necessary.

If you can avoid repeating your code you should do it.

Simply as last-callback

There is a package in NPM called last-callback , which removes need of writing whole callback boilerplate. All you need to do is pass all arguments to lastCallback() function to get extracted last callable argument.

Example for NodeJS:

const lastCallback = require("last-callback");

function myFunc (param1, param2) {
  let error = null;

  // extract last arguments and make it callable
  // you do not need to declare callback as last argument
  // this way you have truly optional callback
  let callback = lastCallback(...arguments);

  /* some logic */

  // you do not need to check if callback is typeof function
  // even if last arguments is not a function
  // lastCallback always returns callable
  // which may do nothing if optional callback has not been passed
  // as last argument.
  // No more type checking
  try {
    if (error) {
      callback(error);
    } else {
      callback(null, param1, param2);
    }
  } catch (err) { /* pass, callback bugs */ }

  /* rest of your logic */
}

Example for browser:

Download last-callback for browsers and add to your project as before your code. Last-callback is dependency free and do not interfere with any libraries like jQuery, Angular, React and other.

<script src="last-callback.v1.0.3.min.js"></script>
<script>
function myFunc (param1, param2) {
  var error = null;

  // ES5 Style:
  var callback = lastCallback.apply(null, arguments);
  // ES6 Style:
  // let callback = lastCallback(...arguments);

  /* some logic */

  try {
    if (error) {
      callback(error);
    } else {
      callback(null, param1, param2);
    }
  } catch (err) { /* pass, callback bugs */ }

  /* rest of your logic */
}
</script>

Pros:

  • Do not need to declare last argument as callback
  • Callback function is automaticaly extracted from arguments list
  • Do not need to check if callback is typeof function
  • If last argument is not a function, last-callback returns void function

Cons:

  • One more dependency in your project