Wind.js : An elegant approach to asynchronies JavaScript

775 views
Skip to first unread message

Tony Huang

unread,
Aug 18, 2012, 10:10:48 AM8/18/12
to nod...@googlegroups.com, je...@live.cn

Brief Introduction

Wind.js is Jeffrey Zhao's effort to simplify async development in JavaScript, which comes with a compiler as well as a set of libraries.

Wear equipments

I'm a game developer, and please allow me to use "Wear equipments" as an example.
When some player wanner to wear an equipment, we will do following steps:

  1. Load player data from database
  2. Check if the player has equipped a equipment at the same slot, if so take it off first
  3. Load equipment data from database
  4. Check the status and ownership of the equipment
  5. Mark the equipment to be "equipped", set the slot of player to the equipment id
  6. Save equipment data to database
  7. Save player data to database

The first workable version

Ok, let's implement this feature.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function equip(characterId, equipmentId, slot, cb) {
  return characters.get(characterId, function(err, character) {
    if (err) return cb(err);
    function afterTakeOff(err) {
      if (err) return cb(err);
      return items.get(equipmentId, function(err, equipment) {
        if (err) return cb(err);
        if (!rules.isCharacterCanWearEquipment(character, equipment)) return cb("PlayerCannotWearEquipment");
        equipment.isEquipped = true;
        player.equipments[slot] = equipment.id;
        return items.save(equipment, function(err) {
          if (err) return cb(err);
          return characters.save(character);
        };
      };
    }
    if (character.equipments[slot]) {
      return takeOff(characterId, slot, afterTakeOff);
    } else {
      return afterTakeOff();
    }
  }
}

Wow, it's really badly smelling!
A lot of "if (err) return cb(err)", callbacks in callbacks, so could this ugly code look better?

Our dream

Some really brilliant guys (just like Anders) are just wondering if it is possible that code in sync way but comes with async benefits, just like this:

01
02
03
04
05
06
07
08
09
10
11
12
13
function equip(characterId, equipmentId, slot) {
  var character = characters.get(characterId);
  if (character.equipments[slot]) takeOff(characterId, slot);
  var equipment = equipment.get(equipmentId);
 
  if (!rules.isCharacterCanWearEquipment(character, equipment)) throw "PlayerCannotWearEquipment";
 
  equipment.isEquipped = true;
  character.equipments[slot] = equipment.id;
 
  equipments.save(equipment);
  characters.save(character);
}

Yes, they did it!

C# 5.0 and async, await keywords

The language designer of C#, Mr. Anders, introduced two new keywords in to C# 5.0, which realised the dream with compiler technology.
Some sync C# code like this:

1
2
3
4
5
void DownloadData() {
  var client = new WebClient();
  var text = client.DownloadString(url);
  Console.WriteLine(text);
}

With just a little bit of modifications:

1
2
3
4
5
async Task DownloadData() {
  var client = new WebClient();
  var text = await client.DownloadStringAsync(url);
  Console.WriteLine(text);
}

It's almost the same, but the smart C# 5.0 compiler will compile this into 2 functions, and the latter one will executed async after the load of http request.

Let's move back to Wind.js

Yes, as what you are guessing, Wind.js (by Jeffrey Zhao) has taken this awesome feature to JavaScript with a fully handwritten compiler as well as a set of async libraries.
We can now transform our code in this way:

01
02
03
04
05
06
07
08
09
10
11
12
13
var equip = eval(Wind.compile('async', function(characterId, equipmentId, slot) {
  var character = $await(characters.get(characterId));
  if (character.equipments[slot]) return $await(takeOff(characterId, slot));
  var equipment = $await(equipment.get(equipmentId));
 
  if (!rules.isCharacterCanWearEquipment(character, equipment)) throw "PlayerCannotWearEquipment";
 
  equipment.isEquipped = true;
  character.equipments[slot] = equipment.id;
 
  $await(equipments.save(equipment));
  $await(characters.save(character));
}));

Isn't it amazing and beautiful?!

Get started with Wind.js on Node.js

First, you need to install wind package with the npm tools:

1
npm install wind

And then, import the wind module to your source file:

1
var Wind = require('wind');

Tips: due to the limitation of current version, you need to name the imported module with exactly 'Wind'

Let's write a simple sample

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function loadData(cb) {
  setTimeout(function() { cb(null, "blabla"); }, 5000);
}
function loadError(cb) {
  setTimeout(function() { cb("Some error"); }, 5000);
}
 
var loadDataAsync = Wind.Async.Binding.fromStandard(loadData);
var loadErrorAsync = Wind.Async.Binding.fromStandard(loadError);
 
var someTestFunction = eval(Wind.compile('async', function() {
  var data = $await(loadDataAsync());
  console.log('Some Result: ' + data);
  try {
    $await(loadErrorAsync());
  } catch(e) {
    console.log('Some Error Occurred: " + e);
  }
}));
 
someTestFunction().start();

Done!

eval === evil?

Almost all textbooks about JavaScript or any other dynamic language that has eval (just like Python) will tell you that: DO NOT USE eval, cos. it's dangerous, dirty and slow!

So why doesn't Wind.js remove the dependency to eval? Will this be dangerous or slow?

If you wanner a simple answer? It is NO, NO, NO, NO!

Let's look back to the origin of the danger of eval: It's mainly because eval is usually used to simplify some process to user input data.

When you work with Wind.js, all code are written by you (ok, there will be your mate's code), and then compiled with the wind compiler, and then eval for just ONCE.

So, you code is only "eval"ed ONCE, and under your control, it's safe and as fast as your handwritten code.

We need your help!

Currently this project's only contributor is its author Jeffrey Zhao, and we need some more people to join us to do the following work:

  1. Compose documents (both in English and Chinese)
  2. Use Wind.js ^.^
  3. Bug reports and advices

We really appreciate all kinds of contributions.

Project Metadata

Author: Jeffrey Zhao
Project Site: http://windjs.org/ (currently only in Simplified Chinese)
GitHub Site: https://github.com/JeffreyZhao/wind
NPM Package: wind

JeanHuguesRobert

unread,
Aug 18, 2012, 10:29:44 AM8/18/12
to nod...@googlegroups.com, je...@live.cn
That's cool.

await & defer are also available in IcedCoffeeScript.

This is an alternative to fibers. I wonder which one provides easier to debug code (ie useful stack traces, etc).

Tony Huang

unread,
Aug 18, 2012, 10:41:44 AM8/18/12
to nod...@googlegroups.com
Well, Wind.js doesn't only generate code for vm, but also generate readable codes ^.^

--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Axel Kittenberger

unread,
Aug 18, 2012, 10:42:17 AM8/18/12
to nod...@googlegroups.com, je...@live.cn
Can you compare wind to Brunos streamline?


I've been happily using that ever since that project of mine that analyzed scientific data out of a mongodb database where classic asynchronous scripting became unbareable soon enough for that use case.

Kind regards, Axel

--

Tony Huang

unread,
Aug 18, 2012, 10:48:25 AM8/18/12
to nod...@googlegroups.com
Love to do that:

I. Compared with IcedCoffeeScript and Streamline
It doesn't need a pre_compiler nor an alternate interpreter.

II. Compared with IcedCoffeeScript
You don't need to learn a new language with completely different syntax compared to JavaScript

Jeffrey Zhao

unread,
Aug 18, 2012, 10:48:38 AM8/18/12
to nod...@googlegroups.com
I’m the author of the library. The main difference between Wind.js and fiber is Wind.js don’t need any extensions to the JavaScript engine, so it works directly in Node.js and also all kinds of JavaScript environment like browsers.
 
The code is easy to debug by attaching a debugger (e.g., eclipse), but the stack trace of an error cannot provide enough information of the failed place right now. The problem looks easier to address in browser since the dev environment is better than Node.js. The code would stop whenever it’s failed, and we can even click on the error in the browser and go to the failed place. I’m still thinking about how to provide a better diagnostic hint.
 
You advices are welcomed.
 

Jeffrey Zhao

unread,
Aug 18, 2012, 11:04:44 AM8/18/12
to nod...@googlegroups.com
They are similar. Some of you guys may remember a project called “Jscex” in the group, that’s just Wind.js before renaming. After that I decided to focus on community in China so most of the contribution went to Chinese. Now more and more project in China are trying to adopt the project since the past year.
 
The main differences between Wind.js and streamline.js are:
 
1. Wind.js has code transformation in runtime, it doesn’t need to have a daemon process to monitor the change and generate the target again. Everything finished with the execution of your code.
 
2. Wind.js generates much more human readable code. Streamline.js generates state machines but Wind.js generates monadic code with input source code as comments. For example:
 
// Original: 
function (interval) {             while (true) {                 drawClock(new Date());                 $await(Wind.Async.sleep(interval));             }         }
 
// Complied:
/* async << function (interval) { */   (function (interval) {                                            var _builder_$0 = Wind.builders["async"];                                            return _builder_$0.Start(this,                                                _builder_$0.While(function () {
/*     while (true) { */                           return true;                                                },                                                    _builder_$0.Delay(function () {
/*         drawClock(new Date()); */                   drawClock(new Date());
/*         $await(Wind.Async.sleep(interval)); */      return _builder_$0.Bind(Wind.Async.sleep(interval), function () {                                                            return _builder_$0.Normal();                                                        });                                                    })
/*     } */                                    )                                            );
/* } */                                })
 
 
3. Wind.js supports different async models directly with different supporting builders/libraries. With the default “async” builder, we write codes target to the build-in task models. We can also use the “promise” builder (even with “async” builder in parallel), then we can “$await” with Promise/A objects, and the object generates by the compiled functions can also be a promise directly. It only requires <40 lines of code to support your own async models. With a different builder we can even has a iteration generator like in JavaScript 1.7 or in Python.
 
Thanks.
Sent: Saturday, August 18, 2012 10:42 PM
Kind regards, Axel


For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Jeffrey Zhao

unread,
Aug 18, 2012, 11:08:19 AM8/18/12
to nod...@googlegroups.com
BTW, we can also use CoffeeScript with Wind.js since CoffeeScript would finally become JavaScript (only a little tricks need to be applied). We can also use IcedCoffeeScript for async features, but Wind.js provide more than normal async support as I described in a separated mail, explaining the differences between Wind.js and Streamline.js.
 
Thanks.
 
 
From: Tony Huang
Sent: Saturday, August 18, 2012 10:48 PM
Subject: Re: [nodejs] Wind.js : An elegant approach to asynchronies JavaScript
 
Kind regards, Axel


For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en
 
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Bruno Jouhier

unread,
Aug 18, 2012, 12:09:20 PM8/18/12
to nod...@googlegroups.com
Hi Jeffrey,

A few corrections regarding streamline:

1) Streamline does not have any kind of daemon process to monitor changes or whatever. You run it like an interpreter: just give the ._js or ._coffee extension to your source file and run it with _node or _coffee. You can also write shell scripts with it (with a shebang). Very much like CoffeeScript (give .cs extension and run with coffee). And like CS it gives you the option to precompile but that's something people usually do only at deployment time, not in the standard dev/debugging cycle.

2) Streamline does not generate state machines. It generates callback patterns that are similar to the ones a programmer would write manually (except that he would probably give up on try/catch). So the generated code is readable. Try it live on http://sage.github.com/streamlinejs/examples/streamlineMe/streamlineMe.html

3) Streamline gives you the choice between 3 code generation options: callbacks, fibers and generators.

Bruno
Kind regards, Axel

  1. Compose documents (both in English and Chinese) <LI style="BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; PADDING-BOTTOM: 0px; BACKGROUND-COLOR: transparent; MARGIN: 0px; OUTLINE-STYLE: none; OUTLINE-COLOR: invert; PADDING-LEFT: 0px;...
    Show original

Jeffrey Zhao

unread,
Aug 18, 2012, 12:30:09 PM8/18/12
to nod...@googlegroups.com
Thanks for the correction, Bruno!
 
1) I just thought in a big picture with Node.js and browsers, but you’re definitely correct in Node.js-only scenarios.
 
2) Please checkout the gist: https://gist.github.com/3388096. For a simple algorithm “bubble sort” with two “for” loops and an “if”, the code generated are quite different. The monadic code generated by Wind.js has nearly the same structure with the input code and has the original input beside it.
 
3) That’s not what I talked about, what I mean is Wind.js can work with different async models, like:
 
var bubbleSort = eval(Wind.compile("async", function (array) {
    for (var i = 0; i < array.length; i++) {
        for (var j = 0; j < array.length - i - 1; j++) {
            var r = $await(compareAsync(array[j], array[j + 1]));
            if (r > 0) $await(swapAsync(array, j, j + 1));
        }
    }
}))
 
Now we are using “async” builder so the $await accepts build-in tasks and bubbleSort() produces the some type of task instaces. We can also:
 
var bubleSort = eval(Wind.compile("promise", function (array) {
    for (var i = 0; i < array.length; i++) {
        for (var j = 0; j < array.length - i - 1; j++) {
            var r = $await(compareAsync(array[j], array[j + 1]));
            if (r > 0) $await(swapAsync(array, j, j + 1));
        }
    }
}))
 
With the “promise” builder the $await accepts Promise/A objects and bubbleSort() also produces Promise/A objects. So if the users have existing codes with Promise/A (or any other ones) as their async model, Wind.js can work directly with it.
--

Bruno Jouhier

unread,
Aug 18, 2012, 12:53:54 PM8/18/12
to nod...@googlegroups.com
Regarding your point 1), there is no difference in the browser: streamline provides a transform API which is just equivalent to the Wind.compile API. I don't understand your point.

In callback mode streamline generates code that is a bit hairier but that's primarily because it sets up a trampoline. So nothing bad will happen if the callback is called synchronously. Does wind.js deal with this case? (async calls that call their callback synchronously)

With the --fibers options, the generated code is much more compact and readable than with either streamline/callbacks or wind.js:


   for (var i = 0; i < array.length; i++) {
        for (var j = 0; j < array.length - i - 1; j++) {
            var r = fstreamline__.invoke(null, compareAsync, [array[j], array[j + 1], _], 2);
            if (r > 0) fstreamline__.invoke(null, swapAsync, [array, j, j + 1, _], 3);
        }
    }
}, 1);
}, 0).call(this, function(err) {
  if (err) throw err;
}));

Bruno
Kind regards, Axel

<DIV style="BACKGROUND-IMAGE: none; BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; PADDING-BOTTOM: 0px !important; LINE-HEIGHT: 1.1em !important; BACKGROUND-COLOR: white; MARGIN: 0px; OUTLINE-STYLE: none !important; OUTLINE-COLOR: invert !important; MIN-HEIGHT: auto !important; PADDING-LEFT: 1em !important; OUTLINE-WIDTH: 0px !important; WIDTH: auto !important;...
Show original

Bruno Jouhier

unread,
Aug 18, 2012, 12:56:55 PM8/18/12
to nod...@googlegroups.com
Ooops, forgot one line in the cut & paste. The --fibers output is:

/*** Generated by streamline 0.4.2 (fibers) - DO NOT EDIT ***/var fstreamline__ = require("streamline/lib/fibers/runtime"); (fstreamline__.create(function(_) { var bubbleSort = fstreamline__.create(function (array, _) {

    for (var i = 0; i < array.length; i++) {
        for (var j = 0; j < array.length - i - 1; j++) {
            var r = fstreamline__.invoke(null, compareAsync, [array[j], array[j + 1], _], 2);
            if (r > 0) fstreamline__.invoke(null, swapAsync, [array, j, j + 1, _], 3);
        }
    }
}, 1);
}, 0).call(this, function(err) {
  if (err) throw err;
}));

Axel Kittenberger

unread,
Aug 18, 2012, 2:02:20 PM8/18/12
to nod...@googlegroups.com
On Sat, Aug 18, 2012 at 6:53 PM, Bruno Jouhier <bjou...@gmail.com> wrote:
Regarding your point 1), there is no difference in the browser: streamline provides a transform API which is just equivalent to the Wind.compile API. I don't understand your point.

I consider more diversity generally a good sign. For example regarding one of my free software projects to my knowledge there is no other alive free software project out there that uses a similar approach - to my dismay. One or the other time something did blink up and when I noted it I took the chance to analyze their code, and get new inspiration and ideas.

So wind got a eval() inside the code. Its not that a big thing to me, certainly achievable with streamline as well, since its javascript itself. Maybe in streamline we're missing a predefined or requireable _eval() call to streamline generate/eval streamlined code on the fly? I haven't yet felt the need for it, but it sounds like a completion to the API.

Input source as comments - as far it isn't there it might be a useful idea to some? I use streamline always as -lp to preserve lines, so for the generated code you get a 1:1 relation to the source code.

Wrapping everything in effectively an eval() call has possibly its merrits, since you can call node directly (with parameters to it, its possible with streamline but needs a little more complicated call to node). Or code that is not streamlined/(un)winded is not touched at all.

I wonder which tool produces the better stack traces? I consider the eval call might be a drawback to that. Other than that still looking for a good comperison that actually doesn't do the usual thing about streamline telling stuff about it, thats just not true.

Marcel Laverdet

unread,
Aug 18, 2012, 3:37:19 PM8/18/12
to nod...@googlegroups.com
As far as I can tell, the differences between Jsex/Wind and Streamline (and for that matter IcedCoffeeScript and TameJS) are largely superficial. The tough part is the compiler, which you can only do so many ways; all other features are just bells and whistles which could be implemented by a user of any library. I prefer Streamline since it seems like Bruno has done a really good job under the hood and it seems cleaner overall. Though personally I just use Futures from node-fibers directly (I mean I'm the author after all).
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en


--
Sent from My iPhone

Jeffrey Zhao

unread,
Aug 18, 2012, 9:24:19 PM8/18/12
to nod...@googlegroups.com
1) I don’t know there’s a transform API in streamline, sorry. If there’s one then using a eval() can do exactly the samething with Wind.js
 
2) What do you mean callback is called synchronously? If you don’t have any async operations in the method body, Wind.js would generate a async task which would be executed synchronously. And yes, with the filter support, we can generate much more compact and readable code. Considering the case of Node.js, I think we can easily add the support in Wind.js as an option.
--

Tony Huang

unread,
Aug 18, 2012, 9:44:06 PM8/18/12
to nod...@googlegroups.com
I prefer Wind.js for following reasons:

1) Compared to Streamline, Wind.js has better syntax
It's easy to compare which one is more readable or native:
Streamline uses a lot of underlines which makes program less readable. Instead, The await syntax of Wind.js is more like native language.

2) Compared to CoffeeScript/IcedCoffeeScript/Streamline, Wind.js doesn't need pre-compilation process
One of the most awesome feature of node.js is it doesn't need compilation. As a result, I can modify the code and test it without any delay, which provide better productivity.
At the deploy time, it's easy to just create a tarball of current source code, upload to the server, and deploy it.
If we use coffeescript or streamline, I have to do either:
a, Deploy _node to the server (only for streamline)
b, Precompile code into javascript

3) Wind.js has better compatibility
I develop my node.js project with the WebStrom IDE from JetBrains. The implementation of Wind made it easy to work with any existing js IDEs. Code completion is just working seamlessly.
At the same time, Wind.js is easily to take effect in existing codes. You don't need to completely rewrite your code in CoffeeScript, or change the file extension to _js

Hope this will be helpful, thank you.
------------------------------------------------------
Tony Huang    cnw...@gmail.com
                     wz...@hotmail.com
                     wz...@vip.sina.com

tjholowaychuk

unread,
Aug 19, 2012, 12:15:31 AM8/19/12
to nodejs
it's called coroutines, skip the syntactic BS
> Tony Huang    cnwz...@gmail.com
>                      wz...@hotmail.com
>                      wz...@vip.sina.com

Axel Kittenberger

unread,
Aug 19, 2012, 3:13:50 AM8/19/12
to nod...@googlegroups.com
On Sun, Aug 19, 2012 at 3:24 AM, Jeffrey Zhao <je...@live.com> wrote:
1) I don’t know there’s a transform API in streamline, sorry. If there’s one then using a eval() can do exactly the samething with Wind.js

For example using callbacks:

-----
var transform = require('streamline/lib/callbacks/transform');

function x(_) {
    console.log('a 1');
    setTimeout(_, 1000);
    console.log('a 2');
    setTimeout(_, 1000);
    console.log('a 3');
    setTimeout(_, 1000);
    return 'go';
};

eval( transform.transform('' + x, {} ) );

x( function( err, ret ) {
    if (err) { throw err; }
    console.log('return: ' + ret);
});
-------
 
2) What do you mean callback is called synchronously? If you don’t have any async operations in the method body, Wind.js ould generate a async task which would be executed synchronously.

Sometimes callback implementations call the callback before they return, for example when they got the answer cached. streamline uses the clever trampoling approach to optimize for that. So in case you got an API that caches, streamline generates more effective code than a normal human coder would (I know there are some people like Tim who are really so good to do it themselves, but for the normal being this is a bon)

And yes, with the filter support, we can generate much more compact and readable code. Considering the case of Node.js, I think we can easily add the support in Wind.js as an option.

For one see 2) if you leave away the trampoline it gets compacter, but is less effective. Also I'd like to see a side-by-side comparison of some examples to objectively see what can be improved. I for one don't look at the generated code at all, since it got 1:1 line any error messages make sense in themselves. Streamline also generates stacktraces that match the original source, which is also quite nice.


Axel Kittenberger

unread,
Aug 19, 2012, 3:22:04 AM8/19/12
to nod...@googlegroups.com
> 2) Compared to CoffeeScript/IcedCoffeeScript/Streamline, Wind.js doesn't
> need pre-compilation process One of the most awesome feature of node.js is it doesn't need compilation.

Seriously it makes a pre-compilation just like the other tools, the
only difference is you call it in an eval rightaway, which can be done
with the others as well if you want to. I suppose you lose correct
linenumbers and filenames in backtraces and errormessage due to this,
don't you?

> As a result, I can modify the code and test it without any delay, which
> provide better productivity.

because calling _node instead of node is a delay?

> At the deploy time, it's easy to just create a tarball of current source
> code, upload to the server, and deploy it.
> If we use coffeescript or streamline, I have to do either:
> a, Deploy _node to the server (only for streamline)
> b, Precompile code into javascript

you need the package in node_modules, thats all about it and is the
same for all tools. And no you don't need it to be globally installed.

> 3) Wind.js has better compatibility
> I develop my node.js project with the WebStrom IDE from JetBrains. The
> implementation of Wind made it easy to work with any existing js IDEs. Code
> completion is just working seamlessly.
> At the same time, Wind.js is easily to take effect in existing codes. You
> don't need to completely rewrite your code in CoffeeScript, or change the
> file extension to _js

To call a file extension a loss in compatiblity is an overstatement,
you do not have to call them _js if you dont want to, it works with
normal .js just as well. It has been a step forward to convince Bruno
to use _js since it isn't exactly javascript anymore, but a mostly
extended javascript alike, which is true for wind.js as well.

Bruno Jouhier

unread,
Aug 19, 2012, 6:53:50 AM8/19/12
to nod...@googlegroups.com
The typical scenario for #2 is caching. Consider the following code:

function loadResource(_, name) {
  return _cache[name] || (_cache[name] = loadResourceAsync(_, name));
}

function loadResources(_, names) {
  var resources = [];
  for (var i = 0; i < names.length; i++)
    resources[i] = loadResource(_, name[i]);
  return resources;
}

The first time you call loadResources, things will be fine because loadResource will execute asynchronously. But if you call it again with a long list of names that have already been loaded before you may blow the stack (because the loop has been turned into a recursion).

Streamline handles this with a trampoline. The problem can also be solved by asking the developer to insert a process.nextTick call into loadResource so that it always returns asynchronously but this is more error prone and less efficient.

Bruno
Kind regards, Axel

    <CODE style="BACKGROUND-IMAGE: none; BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; PADDING-BOTTOM: 0px !important; LINE-HEIGHT: 1.1em !important; MARGIN: 0px; OUTLINE-STYLE: none !important; OUTLINE-COLOR: invert !important; MIN-HEIGHT: auto !important; PADDING-LEFT: 0px !important; OUTLINE-WIDTH: 0px !important; WIDTH: auto !important; PADDING-RIGHT: 0px !important; DISPLAY: inline !important; FONT-FAMILY: consolas,'Bitstream Vera Sans Mono','Courier New',courier,monospace !important; DIRECTION: ltr !important; FLOAT: none !important; FONT-SIZE: 1em !important; VERTICAL-ALIGN: baseline !important; OVERFLOW: visible !important; BORDER-TOP: 0px; BORDER-RIGHT: 0px; PADDING-TOP: 0px !important; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius:...
Show original

Jeffrey Zhao

unread,
Aug 19, 2012, 9:00:01 AM8/19/12
to nod...@googlegroups.com
In Wind.js, if the execute path don’t have any async steps/$await in it, it would be completed synchronously. For example:
 
function abc(i) {
    if (i >= 0) {
        $await(process(i));
    } else {
        return i + 1;
    }
}
 
For all the i smaller than zero, the method would be synchronous, or the method completes asynchonously (strickly speaking, the task inside $await also needs to be async). There wouldn’t be unnecessary callbacks. This behavior is handled by the “async” builder with the build-in task type. Wind.js has a foundational compiling pattern but all of the rest is built as libraries so it’s easy to extend or customize.
--

Dominic Tarr

unread,
Aug 22, 2012, 6:55:41 AM8/22/12
to nod...@googlegroups.com
this is all very clever, but do code transformations really make callbacks easier?

Bruno Jouhier

unread,
Aug 22, 2012, 12:55:15 PM8/22/12
to nod...@googlegroups.com
On Wednesday, August 22, 2012 12:55:41 PM UTC+2, Dominic wrote:
this is all very clever, but do code transformations really make callbacks easier?

Tony Huang

unread,
Aug 23, 2012, 11:31:15 AM8/23/12
to nod...@googlegroups.com
I'm transforming my game server into Wind.js style. And it's really painless and smooth.

Wind.js has already provided the Wind.Async.Bindings.fromStandard and Wind.Async.Bindings.fromCallback stubs to help you integrate existing codes.

For instance, I already have a EntityStore class which provides standard style interface:
EntityStore.prototype = {
  'get': function(cb) {
    //...
  },
  'save': function(entity,cb) {
    //...
   }
};
In order to make it usable in Wind.js, I just added following codes at the end of the module:
['get', 'save'].forEach(function(method) {
  EntityStore.prototype[method + 'Async'] = Wind.Async.Binding.fromStandard(EntityStore.prototype[method]);
});

As a result, I can access these methods in Wind.js easily in this way:

var users = new EntityStore('user');
var currentUser = $await(users.get(userId));

It's quite easy.

But in the other hand, Wind.js hasn't provide the reverse operation, so I added 2 method to the library:
- Wind.Async.Binding.toStandard
- Wind.Async.Binding.toCallback

So I can replace my code method by method without any overhead:
My original code might be:
function wearEquipment(characterId, equipmentId, cb) {
  //..... deep levels of callbacks
}
After refactoring, my code comes to:
var wearEquipmentAsync = eval(Wind.compile('async', function(characterId, equipmentId) {
  // code in synchronised style
});
var wearEquipment = Wind.Async.Binding.toStandard(wearEquipmentAsync);

Completely the same interface, the same function, but better-looking code.

--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en



--

Tony Huang

unread,
Aug 23, 2012, 11:40:20 AM8/23/12
to nod...@googlegroups.com
sorry for a small mistake

On Thu, Aug 23, 2012 at 11:31 PM, Tony Huang <cnw...@gmail.com> wrote:
I'm transforming my game server into Wind.js style. And it's really painless and smooth.

Wind.js has already provided the Wind.Async.Bindings.fromStandard and Wind.Async.Bindings.fromCallback stubs to help you integrate existing codes.

For instance, I already have a EntityStore class which provides standard style interface:
EntityStore.prototype = {
  'get': function(cb) {
    //...
  },
  'save': function(entity,cb) {
    //...
   }
};
In order to make it usable in Wind.js, I just added following codes at the end of the module:
['get', 'save'].forEach(function(method) {
  EntityStore.prototype[method + 'Async'] = Wind.Async.Binding.fromStandard(EntityStore.prototype[method]);
});

As a result, I can access these methods in Wind.js easily in this way:

var users = new EntityStore('user');
var currentUser = $await(users.get(userId));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
should be:
var currentUser = $await(users.getAsync(userId)); 

Bruno Jouhier

unread,
Aug 23, 2012, 12:16:05 PM8/23/12
to nod...@googlegroups.com
If your EntityStore interface uses standard callbacks:

  EntityStore.prototype = {
    get: function(id, cb) { ... },
    save: function(entity, cb) { ... }
  };

Streamline will let you use it "as is". You will be able to write your wearEquipement function as:

  function wearEquipment(characterId, equipmentId, _) {
    // code in synchronised style
    // for example:
    var users = new EntityStore('users');
    var user = users.get(userId, _);
  });

From another streamline function you will call it as:

  var result = wearEquipment(characterId, equipementId, _);

And people who don't use streamline will also be able to call it, just like any other node.js async function (because the transformed function IS a standard node.js async function!):

  wearEquipment(characterId, equipmentId, function(err, result) {
    // usual node.js code here
  });

You do not have to introduce dual APIs with Async variants. You do not need any eval(Wind.compile('async', function(...) {...})) wrapper in every async function that you define, ... You get direct interoperability between streamlined code and regular node.js code.

Bruno

Jeffrey Zhao

unread,
Aug 23, 2012, 12:34:45 PM8/23/12
to nod...@googlegroups.com
You can also directly use Node.js style of API without binding if you need. We just use Task model with “async” builder. As I said, Wind.js supports different async model in parallel, you can choose whatever you prefer for each single async method, like the simple and stand Node.js model, Promise/A model or the Wind.js’s build-in Task model. The Task model just provides more features for common used async patterns (e.g., task cancellation).
 
Thanks
Jeffrey Zhao
 
Sent: Friday, August 24, 2012 12:16 AM
Subject: Re: [nodejs] Wind.js : An elegant approach to asynchronies JavaScript
 

Bruno Jouhier

unread,
Aug 23, 2012, 1:48:07 PM8/23/12
to nod...@googlegroups.com
Not sure I get it. From your samples (https://github.com/JeffreyZhao/wind/blob/master/samples/async/node/copy-dir.js), you need to wrap node's APIs:

fs.existsAsync = Binding.fromCallback(fs.exists);
fs.mkdirAsync = Binding.fromStandard(fs.mkdir);
fs.readdirAsync = Binding.fromStandard(fs.readdir);
fs.statAsync = Binding.fromStandard(fs.stat);
fs.closeAsync = Binding.fromStandard(fs.close);
fs.openAsync = Binding.fromStandard(fs.open);
fs.readAsync = Binding.fromStandard(fs.read);
fs.writeAsync = Binding.fromStandard(fs.write);

With streamline you don't (except for exists which is a black swan :-( ). You can directly call fs.mkdir, fs.readdir, etc.

Also, the xxxAsync functions that you write in Wind style return an instance of Task and you run them with a start call, as xxxAsync(args).start(). Can you call these functions from non-Wind code, with a continuation callback? Something like xxxAsync(args).start(cb);

Also, you need an  eval(Wind.compile('async', function(...) {...}))  wrapper around every async function that you define. This means that you are going to compile and eval every time you create a new closure at runtime. You can probably avoid the recompile (by caching it) but not the eval. Is this right?

Consider

  function f1(x) {
    return eval(Wind.compile('async', function(...) { var y = $await(f2()); return f3(x, y); });
  }

Bruno

Brett Ritter

unread,
Aug 23, 2012, 2:32:46 PM8/23/12
to nod...@googlegroups.com
To all and sundry - PLEASE trim your quotes. This was a particularly
long (said without complaint!) message, but many replies have left it
quoted intact for no purpose.

--
Brett Ritter / SwiftOne
swif...@swiftone.org

Bruno Jouhier

unread,
Aug 23, 2012, 4:13:10 PM8/23/12
to nod...@googlegroups.com
Sorry for that,

I'm responding through the web interface and the entire message is automatically quoted. The quote shows up in collapsed form with just a tiny "-show quoted text-"  link at the bottom of the reply, so I didn't think it was harmful. But maybe some other readers (email clients) show it uncollapsed. I'll try to avoid that in the future.

Quote callback hell :-)

Bruno

Andy

unread,
Aug 23, 2012, 4:14:11 PM8/23/12
to nod...@googlegroups.com
yeah, google groups can get a bit annoying at times

Regarding eval, I'm pretty wary of running your entire async code through it as a string. During this talk on v8 internals at JSConf US, this Russian v8 developer with a long name talks about how v8 runs and optimizes your code. Unfortunately it's not in the video, but during the Q&A section I asked him if v8 could perform the same optimizations for code instantiated with new Function(). He said yes, but it could not optimize eval. I think it had something to do with the ambiguity of the scope eval executes in.

I know this is a vague answer, and I hope I'm not misquoting him. I'd like to see some benchmarks of your compiled code vs the original. And it probably wouldn't be that big of a change to compile it to a capital F Function instead, for which a benchmark would also be useful.

Bruno Jouhier

unread,
Aug 23, 2012, 4:35:29 PM8/23/12
to nod...@googlegroups.com
I see 2 issues here:

1) optimiz-ability of the code generated by eval.
2) the fact that you have to eval every time if the async function is inside a closure.

I don't know how real 1) is but 2) is problematic if you use lots of closures.

Streamline does not have this problem because it transforms at require time (or compile time, your choice) and it does not generate any "eval" nor "new Function" calls when it transforms the code.

Stuart Carnie

unread,
Aug 23, 2012, 4:43:49 PM8/23/12
to nod...@googlegroups.com
Bruno,

Have you experimented with IcedCoffeeScript?  The await / defer syntax is just great.

Cheers,

Stuart

Bruno Jouhier

unread,
Aug 23, 2012, 6:05:32 PM8/23/12
to nod...@googlegroups.com
I know it but TameJS/IcedCoffeeScript came after streamline (I released streamline in January 2011, 5 months before tameJS). We were already using streamline heavily in our project when tameJS came out.

Last time I looked at IcedCoffeeScript, it had limited support for await/defer in subexpressions, it did not support exception handling constructs (try/catch, try/finally), it did not trampoline, etc. (except for the trampoline, streamline had these features from day 1). Also, streamline supports 3 transformation modes: callbacks, fibers and generators.

I recently looked at the tame paper and I was not overly impressed: the AST transform that it describes corresponds to streamline's basic transformation patterns but streamline uses a lot more patterns (see http://bjouhier.wordpress.com/2011/05/24/yield-resume-vs-asynchronous-callbacks/). My goal with streamline was to handle 100% of JavaScript, not a subset.

Streamline's syntax receives a lot of criticisms. I could have used a keyword ('wait' was a good candidate) instead of _ but I wanted a discrete (yet visible) marker that was unlikely to collide with variable or function names. This is why I chose _ (unfortunately I had missed the cool underscore library at the time).

But I don't see any advantage in replacing _ with a pair of await/defer keyword, at least if we set aside considerations of taste. Consider the following:

composition:
  f1(_, f2(_, f3(_, a)))
operators:
  f1(_, a) + 2 * f2(_, b) / f3 (_, c)
  f1(_, a) || f2(_, b) || f3 (_, c)
initializers
  { p1: f1(_, a), p2: f2(_, b) }
  [ f1(_, a), f2(_, a), f3(_, c) ]
chaining:
  f1(_, a).f2(_, b).f3(_, c)
arbitrary combination of the above

How would you write these with defer/await keywords? To me, the keywords won't help. Instead, they will "get in the way".

Bruno

Bruno Jouhier

unread,
Aug 23, 2012, 6:08:42 PM8/23/12
to nod...@googlegroups.com
Hi Stu,

Scrolled too fast and I missed your last name (I need a bigger laptop). Hope you're doing well :-)


Bruno

On Thursday, August 23, 2012 10:43:49 PM UTC+2, Stuart Carnie wrote:

Jeffrey Zhao

unread,
Aug 23, 2012, 8:45:35 PM8/23/12
to nod...@googlegroups.com
Since they are samples of “async” builders. :)
 
We can use fs.mkdir, fs.readdir directly with another builder. Of couse the Task object can be used as conitnuation callback (since everything is normal JavaSript):
 
xxxAsync().on(“complete”, function () { console.log(this.result); }).start();
 
There’s no recompilation during the execution. There’s only one compilation and eval for each single method definition.

Jeffrey Zhao

unread,
Aug 23, 2012, 8:49:43 PM8/23/12
to nod...@googlegroups.com
That’s really a hugh story I’ve talked quite a lot with different people (in China in Chinese, sadly). Basicly there’s no performance lost with the usage in Wind.js since 1) the eval is called only once for each method 2) the benchmark shows that 3) we can do precompilation if you really care about “eval”s.
 
Thanks.
Jeffrey Zhao
 
From: Andy
Sent: Friday, August 24, 2012 4:14 AM
Subject: Re: [nodejs] Wind.js : An elegant approach to asynchronies JavaScript
 
yeah, google groups can get a bit annoying at times
 
Regarding eval, I'm pretty wary of running your entire async code through it as a string. During this talk on v8 internals at JSConf US, this Russian v8 developer with a long name talks about how v8 runs and optimizes your code. Unfortunately it's not in the video, but during the Q&A section I asked him if v8 could perform the same optimizations for code instantiated with new Function(). He said yes, but it could not optimize eval. I think it had something to do with the ambiguity of the scope eval executes in.
 
I know this is a vague answer, and I hope I'm not misquoting him. I'd like to see some benchmarks of your compiled code vs the original. And it probably wouldn't be that big of a change to compile it to a capital F Function instead, for which a benchmark would also be useful.

Bruno Jouhier

unread,
Aug 24, 2012, 2:43:22 AM8/24/12
to nod...@googlegroups.com
Jeffrey,

Can you give a small example of a Wind function that calls fs functions  in sync style, without wrapper (but with the other builder). I'm still puzzled about how this can work. The "complete" event answers the other side of my question.

Regarding eval, I agree that they it will be called only once in the case of functions and methods that are defined at top level in a module. But what about async functions that you define inside other functions (closure case)? In this case, eval will be called every time you call the containing function.

Bruno

Jeffrey Zhao

unread,
Aug 24, 2012, 3:01:37 AM8/24/12
to nod...@googlegroups.com
I'll provide more samples in the repository next week since I’m a little busy in these days. :(
 
You’re right, the function would be compiled and evaled multiple times if it’s in a function would be called again and again. That’s the case should be noticed when using Wind.js in dev mode. So Wind.js also provides a pre-compiler to transform the code before production deployment, or I think we can do something like Streamline to provide an additional command to start the program.
 
Thanks.
 
 
Sent: Friday, August 24, 2012 2:43 PM
Subject: Re: [nodejs] Wind.js : An elegant approach to asynchronies JavaScript
 
Reply all
Reply to author
Forward
0 new messages