JSXのgeneratorで同期的なsleep()を実装してみる

[追記]v0.9.84現在、--enable-generator-emulationが必要です。これを付けないと、ES6のgeneratorを使うようにコンパイルされます。[/追記]

最近、JSの非同期まわりが新しい盛り上がりがありました。
Google Chromeに入ったジェネレータとPromiseで非同期処理に革命が起きた - 素人がプログラミングを勉強していたブログ
2013-05-02

とくにES6のgeneratorを使えば、非同期コードを同期的に書けるようになるということで期待が持てます。
ところで、JSXにも最近実験的にgeneratorが実装されました*1。生成されるJavaScriptはES5準拠ですから、スマートフォンでも実行可能です。
すなわち、これが本格的に使えるようになれば、ブラウザの対応を待たずにgeneratorが使い放題になるというわけですね!generatorを使いこなすのはかなり難しいのですが、静的型付けな分多少は開発もしやすいと思います ;)

なお、JSX generatorの正規リリースする際にはAPIが変更される可能性があります。ですから実験以外での使用は薦められませんのでご注意ください。

さて、このgeneratorを使えば、同期的に呼び出せるsleepは以下のように実装できます。setTimeout()を使った実装なのでもちろんUIをブロックしたりはしません。

/*JSX_OPTS
--enable-generator-emulation
*/

/***
 * Experimental generator
 */

import "timer.jsx";

class Async {
  static function sleep(durationMS : number) : (variant) -> void {
    return (g) -> {
      Timer.setTimeout(() -> {
        Async.go(g);
      }, durationMS);
    };
  }

  static function run(coro : () -> void yield (variant)->void) : void {
    Async.go(coro());
  }

  static function go(v : variant) : void {
    var g = v as Generator.<void, (variant)->void>;

    var data = g.next();
    if (data.done) {
      return; // nothing
    } else {
      data.value(g);
    }
  }
}

class _Main {
  static function main(args : string[]) : void {
    Async.run(function * () : void yield (variant)->void {
      log "H";
      yield Async.sleep(100);
      log "e";
      yield Async.sleep(100);
      log "l";
      yield Async.sleep(100);
      log "l";
      yield Async.sleep(100);
      log "o";
    });
  }
}
// vim: set tabstop=2 shiftwidth=2 expandtab:

XMLHttpRequestの結果を待つこともできます。

      // ...
      var s = "";
      yield (g:variant) : void -> {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/index.html");
        xhr.addEventListener("load", (event) -> {
          s = xhr.responseText;
          A.go(g);
        });
        xhr.addEventListener("error", (event) -> {
          A.go(g);
        });
        xhr.send();
      };
      log "response: " + s;
      // ...

*1:by id:wasabiz++