LoginSignup
43
45

More than 5 years have passed since last update.

Babelのasync/await試してみた(+中の処理をちょっと追ってみた)

Last updated at Posted at 2015-03-12

npm install babel -g

experimentalオプション必須なので コンパイルは babel -e foo.es6 > foo.js みたいな感じ

適当に試してみた。

import "babel/polyfill";

let wait = function(n: number){
  return new Promise(done => setTimeout(() => done(n), n));
};

let main = async function(){
  await wait(50);
  console.log('await done');
}

// async promise nomally
wait(100).then(() => console.log('promise normaly done'));

// await
main();

最初awaitブロックがasyncを取るのが必須なことに気づかなくて、普通に呼ぼうとしてちょっとハマった。まあ処理系やポリフィルの都合そうなるのはなんとなくわかる。

これasync () => {...}await () => {...}みたいな Arrow Functionでも動くのかなぁと思って次のコード試してみた。

import "babel/polyfill";
(async () => {
  await new Promise(done => setTimeout(() => {console.log('done 1'); done()}, 100));
  await new Promise(done => setTimeout(() => {console.log('done 2'); done()}, 100));
})()

じゃあワンライナーもいけるじゃんと試してみたら動いた

import "babel/polyfill";
(async () => await new Promise(done => setTimeout(() => {console.log('wait done'); done()}, 1000)))()

吐かれたコード読んでみる。

"use strict";

var _this = this;

require("babel/polyfill");

(function callee$0$0() {
  return regeneratorRuntime.async(function callee$0$0$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        context$1$0.next = 2;
        return new Promise(function (done) {
          return setTimeout(function () {
            console.log("wait done");done();
          }, 1000);
        });

      case 2:
        return context$1$0.abrupt("return", context$1$0.sent);

      case 3:
      case "end":
        return context$1$0.stop();
    }
  }, null, _this);
})();

あんまり読みたくはないのが出てきたが… とりあえず追っていったところ https://github.com/babel/regenerator-babel/blob/master/runtime.js を使っている。

https://github.com/babel/regenerator-babel/blob/master/runtime.js#L96-L119 をみたところGeneratorってのでwrapしてPromiseをキューにしてさばき終えるのを待ってて、割と素直だった。というかAST読んでからの式変形の実装が強そう。(これCPS変換って言っていいんだろうか)

babel/polyfillがcommonjs requireのままなのでブラウザに持ってくにはbrowserify前提な感じ。

式からawaitの出現数を数えてcase作ってるようにみえたので、forで回すちょっと意地悪なコード書いてみる。

import "babel/polyfill";
(async () => {
 for(var i = 0; i < 10; i++){
   await new Promise(done => setTimeout(() => {console.log('wait done'); done()}, 100));
 }
})();

ちゃんと動いた。が、出力みて頭かしげた。

"use strict";

var _this = this;

require("babel/polyfill");

(function callee$0$0() {
  var i;
  return regeneratorRuntime.async(function callee$0$0$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        i = 0;

      case 1:
        if (!(i < 10)) {
          context$1$0.next = 7;
          break;
        }

        context$1$0.next = 4;
        return new Promise(function (done) {
          return setTimeout(function () {
            console.log("wait done");done();
          }, 100);
        });

      case 4:
        i++;
        context$1$0.next = 1;
        break;

      case 7:
      case "end":
        return context$1$0.stop();
    }
  }, null, _this);
})();

これ、いわゆるswitchによるgoto… なんかllvmがCから出力したIRみたいだ。

なんか納得感あったのでこれで終わり。

43
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
45