Skip to content

Instantly share code, notes, and snippets.

@darscan
Created May 9, 2011 13:49
Show Gist options
  • Save darscan/962548 to your computer and use it in GitHub Desktop.
Save darscan/962548 to your computer and use it in GitHub Desktop.
And so I abandoned the Ruby to Node port after writing this:
Function.prototype.or = function(callback) {
var next = this;
return function(err, doc) {
if (err) {
callback(err, doc);
} else {
next(doc, callback);
}
};
};
@robertpenner
Copy link

Interesting... care to explain?

@darscan
Copy link
Author

darscan commented May 9, 2011

Sure, so Node.js is all about "non-blocking" calls for I/O. This means lots of callbacks. The community has settled on the following approach to callbacks:

function someCallback(err, result) {
  if (err) {
    // handle the error
  } else {
    // handle the result
  }
};

Async function signatures look something like this:

function someFunction(val, callback);

You call it and pass a callback. When it finishes it calls the callback. Unfortunately, when you need to do a bunch of asynchronous things in sequence things become really, really messy:

someFunction("hello", function(err, result) {
  if (err) {
    // handle error
  } else {
    var param = result.someValue;
    someOtherFunction(param, function(err, result) {
      if (err) {
        // handle error
      } else {
        theThirdAsyncFunction(500, function(err, result) {
          if (err) {
            // handle error
          } else {
            console.log(result);
          }
        });
      }
    });
  }
});

Of course, we can blame the nested anonymous functions and refactor thusly:

someFunction("hello", handleSomefunction);

function handleSomefunction(err, result) {
  if (err) {
    // handle error
  } else {
    var param = result.someValue;
    someOtherFunction(param, handleSomeOtherFunction);
  }
};

function handleSomeOtherFunction(err, result) {
  if (err) {
    // handle error
  } else {
    theThirdAsyncFunction(500, handleTheThirdAsyncFunction);
  }
};

function handleTheThirdAsyncFunction(err, result) {
  if (err) {
    // handle error
  } else {
    console.log(result);
  }
};

But what if we don't want to handle errors at every step? Could we chain it up and have any error terminate the flow and be handled elsewhere? Also, notice that our functions are callbacks, not methods (they describe what they handle rather than what they do).

The code in my gist creates callback functions to wrap async methods:

// This is where I want to handle the eventual result or error
function handler(err, result) {
  if (err) throw err;
  console.log(result);
};

// Kick it off
doSomeFunction("hello", handler);

function doSomeFunction(val, callback) {
  someFunction(val, doSomeOtherFunction.or(callback));
};

function doSomeOtherFunction(val, callback) {
  var param = val.someValue;
  someOtherFunction(param, doTheThirdFunction.or(callback));
};

function doTheThirdFunction(val, callback) {
  theThirdAsyncFunction(500, callback);
};

Now the functions are all aync functions rather than callback functions, and the error and final result are handled in one place.

There are tons and tons of flow-control libraries out there (in fact, writing your own is a right of passage in the Node world). This was a light weight solution that worked pretty well for my needs.

@robertpenner
Copy link

Nice one, thanks!

@tschneidereit
Copy link

You should take a look at Spidernode in combination with task.js: Seamless async functions chaining using generators. See slides 37 to 39 of this presentation for details.

@darscan
Copy link
Author

darscan commented Jun 14, 2011

Thanks Till! I'd come across task.js, but not Spidernode. Will bookmark for future reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment