dracoblue.net

Chain + Group Callbacks for NodeJS

If one develops with Node.JS and wants to use the full event-loop power, you got to use callbacks for everything.

That said, it looks ugly to do that:

posix.rename("/tmp/hello", "/tmp/world").addCallback(function () {
  posix.stat("/etc/passwd").addCallback(function (stats) {
    sys.puts("stats: " + JSON.stringify(stats));
  });
});

The

api advices to use:

posix.rename("/tmp/hello", "/tmp/world").wait();
var stats = posix.stat("/etc/passwd").wait();
sys.puts("stats: " + JSON.stringify(stats));

but this is a problem, too. Why? Because you can't do that too often (just ~10 times) at the same time.

That's why I created two little helper methods:

chain and group.

chain(func1,func2,...) : void The chain works that way: It takes nearly unlimited amount of arguments, each of those is a function. Then it executes each of those (in order) and gives them one argument: a callback. As soon as this callback is called, it will call the next one in the chain. And so on.

var stats = null;
[strong]chain(function(cb) {[/strong]
    posix.rename("/tmp/hello", "/tmp/world").addCallback(function () {
        [strong]cb();[/strong]
    });
[strong]}, function(cb) {[/strong]
    posix.stat("/etc/passwd").addCallback(function (chain_stats) {
        stats = chain_stats;
        [strong]cb();[/strong]
    });
[strong]}, function() {[/strong]
    sys.puts("stats: " + JSON.stringify(stats));
[strong]});[/strong]

group(func1, func2, func3, ...) : function The group works pretty much like the chain, except that it does not care much about the order of execution. It just ensures that all functions are called. Also it follows a continous style and returns a function!

The previous example is realized with a group in the following way:

var stats = null;
[strong]group(function(cb) {[/strong]
    posix.rename("/tmp/hello", "/tmp/world").addCallback(function () {
        [strong]cb();[/strong]
    });
[strong]}, function(cb) {[/strong]
    posix.stat("/etc/passwd").addCallback(function (chain_stats) {
        stats = chain_stats;
        [strong]cb();[/strong]
    });
[strong]})(function() {[/strong]
    sys.puts("stats: " + JSON.stringify(stats));
[strong]})[/strong]

Important is, that the call to group returns a function! This function

must be called with one parameter, the callback for the entire group. So it does not matter, wether renaming or stat's for the passwd file are finish first, but when all of them are done: the stats will be printed.

Here comes the code. The code for group+chain is released under the Public Domain.

/**
 * Creates a group of all passed arguments (each of them must be a function)
 * and returns a function, which executes all.
 * 
 * @return function
 */
GLOBAL.group = function () {
    var args = arguments;
    var args_length = args.length;

    return function(cb) {
        if (args_length === 0) {
            cb();
            return ;
        } 

        var items_left_to_execute = args_length;

        var call_group_item = function(arg) {
            arg(function() {
                items_left_to_execute--;
                if (!items_left_to_execute) {
                    cb();
                }
            });
        };

        for ( var i = 0; i < args_length; i++) {
            call_group_item(args[i]);
        }
    };
}

/**
 * Executes all functions (passed as arguments) in order.
 * 
 * @return
 */
GLOBAL.chain = function () {
    var args = arguments;
    var args_length = args.length;

    if (args_length === 0) {
        return ;
    }

    var args_pos = 0;

    var start_func = function() {
        args[args_pos](function() {
            args_pos++;
            if (args_length > args_pos) {
                start_func();
            }
        });
    };

    start_func();
}

In javascript, node.js, open source by @ 17 Feb 2010

comments powered by Disqus

Recent Files

Advertisement

Recent Dev-Articles

Read recently

About

Blogroll