Skip to content

Commit

Permalink
Add examples and rewrite Readme
Browse files Browse the repository at this point in the history
  • Loading branch information
BYVoid committed Dec 8, 2012
1 parent 185985c commit b88c92f
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 14 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#Change Log

## 0.1.0

2012-12-8

* Implemented parallel statement, lightweight thread supported
* Load additional modules lazily in command line
* Refine many parts of code

## 0.0.8

2012-12-5
Expand Down
259 changes: 245 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Continuation.js

Continuation.js is a compiler for asynchronous [Continuation-Passing Style](http://en.wikipedia.org/wiki/Continuation-passing_style) transformation, which simplifies asynchronous JavaScript programming.
Continuation.js is a compiler for asynchronous [Continuation-Passing Style](http://en.wikipedia.org/wiki/Continuation-passing_style) transformation, which simplifies asynchronous JavaScript programming. It can be also called a translator, because it translates slightly flavored JavaScript syntax into standard JavaScript. Continuation.js introduces a virtual function ``cont``, which allow you to write continuation-passing style code (or asynchronous callback style code) far easier. `cont` is not a actual function, but a mark with the same syntax to function in JavaScript. By using it you can write asynchronous control flows like flat threaded code, and Continuation.js compiles it into continuation-passing style code.

Typically, writing with asynchronous control flow is a pain because you will easily write nested callbacks like below:
Typically, writing with asynchronous control flow is a pain, because you will easily write nested callbacks like below:

```javascript
function textProcessing(callback) {
Expand Down Expand Up @@ -45,7 +45,9 @@ if (err)
console.error(err);
```

Or even simpler:
The code above is flatted by using virtual function call ``cont``. The control flow must "wait" for return of the asynchronous function call ``fs.readFile``. And parameters in the argument list of ``cont`` will be set after it returns. "Return" here is a little confusing because in an asynchronous function "return" means callback function called, not "return" in the literal sense. Because an asynchronous function usually returns immediately (by encountering ``return`` statement or the end of the function scope) while the callback function could be called later. You can be understood as all lines after ``cont`` until the end of the function are the callback function of the asynchronous function call. The code feels like threaded code, but it is still asynchronous while executing.

Even more simply, the asynchronous error can be held with JavaScript ``try...catch`` syntax and another virtual function call ``obtain`` provided by Continuation.js. ``obtain(a)`` is equivalent to ``cont(err, a)``, and ``err`` would be thrown if it is not ``undefined``.

```javascript
function textProcessing(ret) {
Expand All @@ -70,13 +72,166 @@ try {
## Features

* A JIT (Just-in-time compilation) and AOT (Ahead-of-time) compiler
* No runtime dependences
* No additional non-native JavaScript syntax
* Flexible coding style
* Readable and debuggable compiled code
* Compatible with [CoffeeScript](http://coffeescript.org/) and [LiveScript](http://livescript.net/)
* Strictly no runtime dependence
* No additional non-native JavaScript syntax and very few reserved keywords (only ``cont``, ``obtain`` and ``parallel``)
* Flexible coding style and readable compiled code
* Compatible with [CoffeeScript](http://coffeescript.org/) and [LiveScript](http://livescript.net/) (and other compile-to-js language)
* Supports both Node.js and browser-side JavaScript
* Parallel execution supported
* Supports execution in parallel and lightweight thread

## Documentation

### cont

``cont`` is a mark of asynchronous calls. It must be used in the place of callback function parameter of a function call. The parameters of ``cont`` will be set to the values of parameters of the callback function. If parameter of ``cont`` is a variable (not an expression), it will be defined automatically before the function call.

Example:

```javascript
setTimeout(cont(), 1000);

fs.lstat('/path', cont(err, stats));

var obj;
fs.readdir('/path', cont(err, obj.files));
```

Compiled code:

```javascript
var err, stats, obj;
setTimeout(function () {
fs.lstat('/path', function () {
err = arguments[0];
stats = arguments[1];
fs.readdir('/path', function () {
err = arguments[0];
obj.files = arguments[1];
});
});
}, 1000);
```

### obtain

``obtain`` is a syntax sugar of ``cont`` and ``throw``. You can use ``try`` to catch asynchronous errors with ``obtain``. One assumption is that the error object it the first parameter of the callback function (this is a convention of Node.js API).

Example:

```javascript
function f1() {
fs.readdir('/path', obtain(files));
}

function f2() {
fs.readdir('/path', cont(err, files));
if (err)
throw err;
}
```

Compiled code:

```javascript
function f1() {
var _$err, files;
fs.readdir('/path', function () {
_$err = arguments[0];
files = arguments[1];
if (_$err)
throw _$err;
});
}
function f2() {
var err, files;
fs.readdir('/path', function () {
err = arguments[0];
files = arguments[1];
if (err) {
throw err;
}
});
}
```

### parallel

``parallel`` allows you to execute asynchronous functions "in parallel". ``parallel`` is also a "virtual function" and its parameters should all be function calls with ``cont`` or ``obtain``.
All function calls in ``parallel`` will be executed in parallel and control flow continues executing the next statement after all the parallel function calls "return" (precisely speaking callback function called).

Note that both Node.js and browser execute JavaScript code in one thread, so this is not a multithreaded implementation.
They feel like threads so they can be called "lightweight threads".
Only I/O and computing are executed in parallel while computing are actually executed in sequence.
That is, when one "thread" is "blocked" by I/O, the other one runs.
The "threads" can automatically utilize the gaps of I/O and computing.
So you should let functions that both have I/O and computing run in parallel, not all functions with heavy computing and little I/O.

Example:

```javascript
var contents = {};
parallel(
fs.readFile('/path1', obtain(contents.file1)),
fs.readFile('/path2', obtain(contents.file2)),
fs.readFile('/path3', obtain(contents.file3))
);
console.log(contents);
```

### Explicit mode

Modules can be written in Continuation.js and they will be recursively compiled automatically when using ``require``. Add ``'use continuation'`` into your source file, and use ``continuation script.js --explicit`` to run it and only files contains ``'use continuation'`` will be compiled. This option can reduce loading time if many modules are recursively required.

### Compilation cache

By using ``contination script.js --cache [cacheDir]``, all modules compiled by Contination.js will be cached into ``cacheDir``. If your source file is newer than the cached version, it will be compiled again. This rely on the system clock and timestamp. This option can also reduce loading time. It is recommended to use ``--explicit`` and ``--cache`` together.

By default ``cacheDir`` is ``/tmp/continuation``.

### Use Continuation.js in CoffeeScript (and other compile-to-js language)

Continuation.js is compatible with most kinds of compile-to-js language because it introduces no non-primitive syntax. The only 3 keywords ``cont``, ``obtain`` and ``parallel`` are all virtual functions so you can use function call directly in your language.

Example (CoffeeScript):

```coffeescript
dns = require('dns')
domains = ['www.google.com', 'nodejs.org', 'www.byvoid.com']
for domain in domains
dns.resolve domain, obtain(addresses)
console.log addresses
```

Until now [CoffeeScript](http://coffeescript.org/) and [LiveScript](http://livescript.net/) are supported by default.

### Use Continuation.js programmatically

You are able to use Continuation.js programmatically. Continuation.js module has one interface function ``compile(code)``. ``code`` must be a string.

Example:

```javascript
var continuation = require('continuation');

function fibonacci() {
var a = 0, current = 1;
while (true) {
var b = a;
a = current;
current = a + b;
setTimeout(cont(), 1000);
console.log(current);
}
};

var code = fibonacci.toString();
var compiledCode = continuation.compile(code);
console.log(compiledCode);
eval(compiledCode);
fibonacci();
```

You can run the code above using ``node`` command. That means you don't have to install Continuation.js in global. The code above converts a function into a string, and then it is compiled by Continuation.js. After that you can run it via ``eval`` function.

## Installation

Expand All @@ -86,7 +241,7 @@ Install Continuation.js with [NPM](https://npmjs.org/package/continuation):

## Usage

Usage: continuation [options] <file.js/file.coffee> [arguments]
Usage: continuation [options] <file.js/file.coffee/file.ls> [arguments]

Options:

Expand All @@ -98,15 +253,31 @@ Install Continuation.js with [NPM](https://npmjs.org/package/continuation):
-c, --cache [directory] run and cache compiled sources to [directory], by default [directory] is /tmp/continuation
-v, --verbose print verbosal information to stderr

## Documentation
Run code written with Continuation.js (i.e. script.js):

contination script.js

Print compiled code on console:

contination script.js -p

Compile code and save as another file:

contination script.js -o compiled.js

Run code in "explicit mode" (only compile sources with ``'use continuation'``. see documentation):

Continuation.js is still under development. Most functionalities are not fully implemented or tested.
contination script.js -e

More details are to be written.
Cache compiled code (including recursively required modules) to accelerate loading (see documentation):

contination script.js -c

## Examples

Calcuating Fibonacci sequence and printing one number by every one second:
### Loops and sleep

Calculating Fibonacci sequence and printing one number by every one second:

```javascript
var fib = function () {
Expand All @@ -122,6 +293,8 @@ var fib = function () {
fib();
```

### Run asynchronous functions in sequence

Read 5 files in sequence:

```javascript
Expand All @@ -135,4 +308,62 @@ for (var i = 0; i < 4; i++) {
console.log('Done');
```

### Run asynchronous functions in parallel

Do things (with heave I/O) in parallel:

```javascript
var fs = require('fs');
var dns = require('dns');
var http = require('http');

var complexWork = function (next) {
setTimeout(cont(), 500);
http.get('http://www.byvoid.com', cont(res));
next(null, res.headers);
};

parallel(
fs.readdir('/', obtain(files)),
dns.resolve('npmjs.org', obtain(addresses)),
complexWork(obtain(result))
);

console.log(files, addresses, result);
```

### Recursions

Calculating disk usage:

```javascript
var fs = require('fs');

function calcDirSize(path, callback) {
var dirSize = 0, dirBlockSize = 0;
fs.readdir(path, obtain(files));
for (var i = 0; i < files.length; i++) {
var filename = path + '/' + files[i];
fs.lstat(filename, obtain(stats));
if (stats.isDirectory()) {
calcDirSize(filename, obtain(subDirSize, subDirBlockSize));
dirSize += subDirSize;
dirBlockSize += subDirBlockSize;
} else {
dirSize += stats.size;
dirBlockSize += 512 * stats.blocks;
}
}
callback(null, dirSize, dirBlockSize);
}

var path = process.argv[2];
if (!path) path = '.';

calcDirSize(path, obtain(totalSize, totalBlockSize));

console.log('Size:', Math.round(totalSize / 1024), 'KB');
console.log('Actual Size on Disk:', Math.round(totalBlockSize / 1024), 'KB');
```

More examples are available in 'examples' directory and 'test' directory.
18 changes: 18 additions & 0 deletions examples/jit/jit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var continuation = require('continuation');

function fibonacci() {
var a = 0, current = 1;
while (true) {
var b = a;
a = current;
current = a + b;
setTimeout(cont(), 1000);
console.log(current);
}
};

var code = fibonacci.toString();
var compiledCode = continuation.compile(code);
console.log(compiledCode);
eval(compiledCode);
fibonacci();
17 changes: 17 additions & 0 deletions examples/parallel/parallel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
var fs = require('fs');
var dns = require('dns');
var http = require('http');

var complexWork = function (next) {
setTimeout(cont(), 500);
http.get('http://www.byvoid.com', cont(res));
next(null, res.headers);
};

parallel(
fs.readdir('/', obtain(files)),
dns.resolve('npmjs.org', obtain(addresses)),
complexWork(obtain(result))
);

console.log(files, addresses, result);

0 comments on commit b88c92f

Please sign in to comment.