npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cps

v1.0.2

Published

A continuation passing style library to help the coding of asynced programs in Javascript/Node.js.

Downloads

12,601

Readme

cps

A CPS (Continuation Passing Style) library to ease the event-driven/asynchronized coding style in node.js. There seems to be enough node.js libs (e.g. async) doing the same thing, why yet another? This lib is notably different from other libs in exception handling. Using cps:

  • Any "throw" statements in the procedures will be transformed to an application of the callback onto the error object.
  • A "rescue" function is provided, which can be used to catch such exceptions pass through the callbacks.

This consistently recovers the try/catch functionality in continuation passing style programming.

Install

npm install cps

Terminologies

Callback

We call a function of the following form a callback:

function(err, res) {
  // process the err and res
}

A callback is a function that takes two arguments, "err" and "res". Semantically, a non-null "err" corresponds to a program exception; while a null "err" corresponds to normal return without any exceptions.

Procedure

We call a function of the following form a procedure:

function(arg1, arg2, ..., callback) {
  // do some work with the arguments and then invoke the callback to continue
}

A procedure is a function that takes a callback as the last argument. Semantically, a procedure does some work with the input arguments and at some point, call the callback to continue. Note that a call to the "callback" argument MUST always be a tail call. In particular, the following is a procedure:

function(callback) {
  // do some work and then invoke the callback to continue
}

API Document

Sequence a list of procedures. Note that the result of each procedure is fed into the next procedure in the listed order.

Example

var concatFile = function(f1, f2, resFile, cb) {
    var contentOfA, contentOfB;
    
    cps.seq([
        function(_, cb) {
            fs.readFile(f1, cb);
        },
        function(res, cb) {
            contentOfA = res;
            fs.readFile(f2, cb);
        },
        function(res, cb) {
            contentOfB = res;
            fs.writeFile(resFile, contentA + contentB, cb);
        }
    ], cb);
};

An asynchronized version of while loop.

Example

Consider a world in which arithmatic operations do not exists and must be accomplished through alien technology. Then the Fibonacci function needs to be written in the following way:

var alienAdd = function(a, b, cb) {
    setTimeout(function() {
        cb(null, a + b);
    }, 0);
};

var asyncFib = function(n, cb) {
    if (n < 0) {
        throw new Error('fib input error');
    }
    if (n == 0) {return cb(null, 1);}
    if (n == 1) {return cb(null, 1);}

    var a = 1, 
    b = 1, 
    i = 2;
    cps.seq([
        function(_, cb) {
            cps.pwhile(
                function(cb) {
                    cb(null, i <= n);
                },
                function(cb) {
                    cps.seq([
                        function(_, cb) {
                            alienAdd(a, b, cb);
                        },
                        function(res, cb) {
                            a = b;
                            b = res;
                            alienAdd(i, 1, cb);
                        },
                        function(res, cb) {
                            i = res;
                            cb();
                        }
                    ], cb);
                },
                cb
            );
        },
        function(_, cb) {
            cb(null, b);
        }
    ], cb);
};

Apply a procedure on an array sequentially.

Example

Then in the same "arithmetic-less" world, print out the first 10 Fibonacci numbers.

cps.peach(
    [1,2,3,4,5,6,7,8,9,10],
    function(el, cb) {
        cps.seq([
            function(_, cb) {
                asyncFib(el, cb);
            },
            function(res, cb) {
                console.log(res);
                cb();
            }
        ], cb);

    },
    cb
);

Apply a procedure on an array sequentially, and record the results in another array, which is pass to the callback.

Example

You can also map it out first and then log the result list.

cps.seq([
    function(_, cb) {
        cps.pmap(
            [1,2,3,4,5,6,7,8,9,10],
            function(el, cb) {
                asyncFib(el, cb);
            },
            cb
        );
    },
    function(res, cb) {
        console.log(res);
        cb();
    }
], cb);

Apply a procedure on a sequence of consecutive indices, starting of 0.

Example

var sum = 0;
cps.seq([
    function(_, cb) {
        console.log('here');
        cps.pfor(10, function(i, cb) {
            setTimeout(function() {
                sum += i;
                console.log(sum);
                cb();
            }, 1000);
        }, cb);
    },
    function(_, cb) {
        console.log(sum);
        cb(null, 'ok');
    }
], cb);

An asyned version of try/catch. It take two procedures as arguments. If the first one fails, the second is executed to rescue.

Example

What if there's some invalid input? Let's catch it without disturbing the overall flow.

cps.seq([
    function(_, cb) {
        cps.pmap(
            [1,2,3,4,5,6,7,8,9,10, -1],
            function(el, cb) {
                cps.rescue({
                    'try': function(cb) { // try clause
                        asyncFib(el, cb);
                    },
                    'catch': function(err, cb) { // catch clause
                        console.log(err);
                        cb(null, -1);
                    },
                    'finally': function(cb) { // finally
                        console.log("always print this whether it's good or bad.");
                        cb(); 
                    }
                }, cb);
            },
            cb
        );
    },
    function(res, cb) {
        console.log(res);
        cb();
    }
], cb);

Parallel a list of procedures. The top level callback is only called after each parallel procedure finishes, regardless the procedure succeeds or fails. The callback will never take a non-null error parameter; the result parameter is an array of the following form:

[
   {"status": "ok", "data": res},  /* in case the procedure in the 
                                      corresponding position succeeds 
                                      with result res
                                   */
   {"status": "error", "error": err}  /* in case the procedure in the 
                                         corresponding position fails 
                                         with error err
                                      */
   // ...
]

Example

See "thread b" being printed out before "thread a":

var parallelTest = function(cb) {
    cps.parallel([
        function(cb) {
            setTimeout(function() {
                console.log('3');
                cb(new Error('kaz'));
            }, 3000);
        },
        function(cb) {
            setTimeout(function() {
                console.log('1');
                cb(null, 'ok');
            }, 2000);
        },
        function(cb) {
            setTimeout(function() {
                console.log('2');
                cb(new Error('foobar'));
            }, 1000);
        }
    ], cb);
};

Running this procedure will yield the following output:

2
1
3
[ { status: 'error', error: [Error: kaz] },
  { status: 'ok', data: 'ok' },
  { status: 'error', error: [Error: foobar] } ]