JavaScript
babel

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

More than 3 years have passed since last update.

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みたいだ。

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