Wind.js : An elegant approach to asynchronies JavaScript

778 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"];