node package manager

sexy

Sexy

A control-flow library for making sequential asynchronous in a synchronous style format using ECMAScript 5 Proxies and Tim Caswell's (@creationix) Step, http://github.com/creationix/step .

Alpha Alert

This is an alpha release of Sexy. Please submit all bugs to http://github.com/CrabDude/sexy/issues

How to install

npm install sexy

Or copy or link the lib/sexy.js file into your $HOME/.node_libraries folder.

How to use

Sexy utilizes ECMAScript 5 Proxies to allow nested asynchronous code to be written in a synchronous style format.

Sexy exports a single function I call $xy. $xy() initializes a sequence and 2 arguments: the initial object to wrap and an onError callback.

$xy(fs,function(e) {if (e) throw e;});

Function callbacks are handled automatically by Sexy and the results from the previous step are passed to the next.

$xy(fs).readdir(__dirname)(function(filenames) {
	// do something
});

Additionally, any errors generated by a step in the sequence will be passed to the onError callback from initialization.

The resulting $xy instance can then be used to write asynchronous code in a synchronous format.

$xy(fs,function(e) {
		// log error
		console.log('ERROR');
		if (e) throw e;
	}).readdir(__dirname)(function(filenames) {
		console.log(SERIOUSLY.DOESNOTEXIST);
	});

Here is an example that outputs the contents of all "*.js" files in a directory:

Standard Node.js code:

$xy(fs,function(e) {if (e) throw e;})
	.readdir(__dirname)
	.forEach(function(filename) {
		if (/\.js$/.test(filename)) {
			fs.readFile(__dirname + "/" + filename, 'utf8', this.parallel());
		}
	})
	(console.log)
	(function() {console.log('hello world');});

Step equivalent:

Step(
	function readdir() {
		fs.readdir(__dirname, this);
	},
	function readFiles(err, results) {
		if (err) throw err;
		results.forEach(function (filename) {
			if (/\.js$/.test(filename)) {
				fs.readFile(__dirname + "/" + filename, 'utf8', this.parallel());
			}
		});
	},
	function showAll(err , file1, file2, file3) {
		if (err) throw err;
		console.log(arguments);
	},
	function() {
		if (err) throw err;
		console.log('hello world');
	}
);

AN ATTEMPT at the standard Node.js equivalent:

// This could certainly be written better
// Step provides numerous wrapping features that make writing an equivalent example cumbersome like wrapping your callbacks in try.. catch blocks.

fs.readdir(__dirname, function(err, results) {
	if (err) throw err;
	function callback(err, file) {
		if (err) throw err;
		function callback2() {
			if (err) throw err;
			console.log('hello world');
		}
		files.push(file);
		if (++counter != total) {
			return;
		}
		console.log(files);
	}
	var files = [], counter = 0, total = 0;
	results.forEach(function (filename) {
		if (/\.js$/.test(filename)) {
			total++;
			fs.readFile(__dirname + "/" + filename, 'utf8', this.parallel());
		}
	});
});

Note that all callbacks can utilize the Step API as Sexy wraps all callbacks in a Step call. As a result, utilizing this and this.parallel and return values, BUT NOT this.group YET, all function as they do in Step. Please refer to the Step README for the Step documentation: http://github.com/creationix/step .

A few key differences in Sexy are that the original binding for this is available at this.that in the callback. Support for this.that at the moment is a little buggy.

Sexy assumes that a function call on a non-function result is a sequential call outside of the current context. Two examples of this functionality are the last two lines of the Sexy example.

$xy(fs,function(e) {if (e) throw e;})
	...
	(console.log)
	(function() {console.log('hello world');});

The first "console.log" is passed the resulting arguments from the previous callback, in this case the contents of the files. In the second, an anonymous function is used to execute an arbitrary final command once the sequence is complete.

In the event that the previous function call, beit from an object or an anonymous sequential call, returns a function, to call that function you must pass an undefined value or the $xy.undef convenience value as the first argument. This is one of a few gray areas as mentioned below. Please submit API recommendations as a feature request to http://github.com/CrabDude/sexy/issues .

var objectX = {
	returnArbitraryFunction: function () {
		// do something fancy
		return function() {
			console.log('arbitrary function');
		};
	}
}

// Execute next function sequentially
$xy(objectX).returnArbitraryFunction()(function() {
	// this WILL NOT call the function returned by objectX.returnArbitraryFunction()
});

// Call returned function
$xy(objectX).returnArbitraryFunction()($xy.undef,function() {
	// this WILL call the function returned by objectX.returnArbitraryFunction()
});

API Gray Areas / Ambiguity

Please submit API recommendations as a feature request to http://github.com/CrabDude/sexy/issues