continuate

0.8.2 • Public • Published

continuate

Build Status

A wrapper to convert regular functions to continuation-passing style.

Usage

//regular divide function which throws error if second arg is 0
function divide(a, b) {
  if (b === 0) {
    throw Error('b is 0');
  }
  return a / b;
}

var cps = require('continuate');

var cpsDivide = cps(divide);

cpsDivide(46, 2, function (err, data) {
  console.assert(data === 23);
});

//it provides error to callback
cpsDivide(1, 0, function (err, data) {
  console.assert(err === true);
});

Description

To avoid callback hell you usually work with modules like async and its methods. Assume that you have a pretty complex function built upon async#auto or async#parallel, whatever. Here we read two data sources and pass data to data-handlers. If data-handlers are pretty complex we will eventually want them to be wrapped in try/catch to avoid sudden failing of our app:

var async = require('async');

function handleData1(dbData) {
  // very complex logic to rebuild dbData
}

function handleData2(webServiceData) {
  // very complex logic to rebuild webServiceData
}

async.auto({
  'dataSource1': [function (callback) {
    readDatabase(callback);
  }],
  'dataSource2': [function (callback) {
    getDataFromWebService(callback);
  }],
  'actionsWithData1': ['dataSource1', function (callback, results) {
    try {
      var data = handleData1(results.dataSource1);
    } catch (err) {
      return callback(err);
    }
    callback(null, data);
  }],
  'actionsWithData2': ['dataSource2', function (callback, results) {
    try {
      var data = handleData2(results.dataSource2);
    } catch (err) {
      return callback(err);
    }
    callback(null, data);
  }],
});

Here we can see that code is not DRY and looks pretty ugly. We can rewrite those handler in continuation-passing style (CPS) right away but imaging situation when you need those handlers as a regular functions, say in synchronous code. You will not want additional callback level in your neat synchronous code.

Instead we can rewrite handling of data in prettier way using continuate module. It is an utility you can use to easily convert regualr functions to CPS:

var async = require('async');
var cps = require('continuate');

//...

async.auto({
  'dataSource1': [function (callback) {
    readDatabase(callback);
  }],
  'dataSource2': [function (callback) {
    getDataFromWebService(callback);
  }],
  'actionsWithData1': ['dataSource1', function (callback, results) {
    var cpsStyle_handleData1 = cps(handleData1);
    //Now it provides error and result in the callback
    cpsStyle_handleData1(results.dataSource1, callback);
  }],
  'actionsWithData2': ['dataSource2', function (callback, results) {
    cps(handleData2)(results.dataSource2, callback);
  }],
});

And we have neat and descriptive logic without changing original handleData1 and handleData2 functions so we can reuse them in regular and CPS-code.

Note, that when you use CPS, functions doesn't become 'asynchronous'. They just provide their results in callbacks.

Consider this example:

var cps = require('continuate');

function add(a, b) { return a + b; }

var cpsAdd = cps(add);
cpsAdd(1, 42, function (err, data) {
  console.assert(data === 43);
});

Since it only returns new function it can be also used with methods. Simple Function.prototype.bind helps:

var cps = require('continuate');

var object = {
  counter: 0,
  add: function (x) { return this.count += x; }
}

var cpsAdd = cps(object.add.bind(object));
cpsAdd(1, function (err, res) {
  console.assert(res === 1);
});

Package Sidebar

Install

npm i continuate

Weekly Downloads

4

Version

0.8.2

License

MIT

Last publish

Collaborators

  • franza