看看编译后async/await
发布于 4 年前 作者 i5ting 9289 次浏览 来自 分享

我也来凑个热闹,看看编译后async/await

在koa 2里使用async

'use strict';

var http = require('http');
var koa = require('koa');
var app = new koa();


// number of middleware

var n = parseInt(process.env.MW || '1', 10);
console.log('  %s async middleware', n);

while (n--) {
  app.use(async (ctx, next) => {
    await next();
  });
}

var body = new Buffer('Hello World');

app.use(async (ctx, next) => {
  await next();
  ctx.body = body;
});


// var apprequire('./koa2-async.js')
app.listen(3333);

module.exports = app

使用babel编译后,如下

'use strict';

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; }

var http = require('http');
var koa = require('koa');
var app = new koa();

// number of middleware

var n = parseInt(process.env.MW || '1', 10);
console.log('  %s async middleware', n);

while (n--) {
  app.use((() => {
    var ref = _asyncToGenerator(function* (ctx, next) {
      yield next();
    });

    return function (_x, _x2) {
      return ref.apply(this, arguments);
    };
  })());
}

var body = new Buffer('Hello World');

app.use((() => {
  var ref = _asyncToGenerator(function* (ctx, next) {
    yield next();
    ctx.body = body;
  });

  return function (_x3, _x4) {
    return ref.apply(this, arguments);
  };
})());

// var apprequire('./koa2-async.js')
app.listen(3333);

module.exports = app;

看一下_asyncToGenerator函数,格式化后

function _asyncToGenerator(fn) {
    return function() {
        var gen = fn.apply(this, arguments);
        return new Promise(function(resolve, reject) {
            function step(key, arg) {
                try {
                    var info = gen[key](arg);
                    var value = info.value;
                } catch(error) {
                    reject(error);
                    return;
                }
                if (info.done) {
                    resolve(value);
                } else {
                    return Promise.resolve(value).then(function(value) {
                        return step("next", value);
                    },
                    function(err) {
                        return step("throw", err);
                    });
                }
            }
            return step("next");
        });
    };
}

很明显,把async函数通过高阶函数转为promise里嵌入的generator来执行的。

整体来说,随着中间件越多,它的性能越差,可以参见 koa、koa2、koa2-async和express的压测性能比较

再看await

app.use(async (ctx, next) => {
  await next();
  ctx.body = body;
});

翻译后

app.use((() => {
  var ref = _asyncToGenerator(function* (ctx, next) {
    yield next();
    ctx.body = body;
  });

  return function (_x3, _x4) {
    return ref.apply(this, arguments);
  };
})());

很明显,await只是yield的别名

结论

  • async/await不需要generator,自己带执行器
  • 翻译后的await = yield

目前Thunk和Promise是一样,都可以接到yield后面,但await的native里的具体实现里还不好说。

另外据mikeal说,10月份可能会有async/await的native实现,拭目以待吧

如有不对的地方,请指正

14 回复

你拿这破编译的有啥意思 自豪地采用 CNodeJS ionic

@captainblue2013 我不知道async/await还咋实现,请指教

v8实现的async/await ,个人猜测不会只是js层面的语法糖,应该会在底层做处理 From Noder

跟楼上一些观点一样,我也觉得 babel 的结果跟 V8 最终的实现不一定是一致的。babel 受限于目前 v8 的能力,无论用了 promise 还是 yield 都只能看做是一种对现实的妥协。

最终 async/await 的性能,以及与 generator 的差异,还是要看 v8 的实现。

@alsotang

Async/await should arrive – behind an ‘experimental’ flag – in Chrome 53, which is scheduled to be released into stable channel on 6 September, so will hopefully arrive (behind a flag) in Node v7, in October.

考虑到手机浏览器,这些都是渣渣,做过微信开发的都知道。

Q2 V8 会完成 async/await 的实现,Node 后面也会跟进吧,现在 babel 为了让大家体验新标准,做了很多 wrap 和 hack ,代码,速度上肯定是有损的。

等V8能解释async/await,这个编译器就不会编译async/await 了吧

举个例子,Chakra: 如果翻了Chakra的代码,就会发现yield和await在token解析时大多相邻甚至写在一起,最后到下面那个函数都以类型Js::OpCode::Yield进行处理。所以整块Chakra中,出现await的基本只有Parser部分。 untitled1.pnghttps://github.com/Microsoft/ChakraCore/blob/3254c38a313f4adfff87c887837ed870fd9e5023/lib/Runtime/ByteCode/ByteCodeEmitter.cpp#L11010)

(非专业,如有错误,敬请指出)

本来就是语义层面的东西。。。为啥要下降到实现里讨论呢。。。先前yield编译还变成一堆switch呢,但是这有什么影响吗? await这种东西一看就明白了,yield看了之后还要考虑是不是有别的用法。除了常见的迭代器,之前我还用它做过一个有限状态机的核心部分,这种情况下yield就不能当作await用了。

来自酷炫的 CNodeMD

@codehz

如果真要讲语义,是js吸收其他语言的东西有点饥不择食,yield和await选其一在语义上都没问题,但js吸收了两,这很自然就出现了各人喜好的问题。谁也没硬性规定yield不能干await的事情。同样对var和let,真需要用两个关键字来区分么?像lua这种本身就支持块级作用域的不用一个local就搞定了?

本质上,这是语言设计的问题。一门设计本身不完备,又处于利益场的中心,看似万千宠爱,其实身不由己,是个大公司都想加点自己的东西进去,以后真一年一个版本了,这个问题恐怕会更加突出。C++变得越来越复杂或许是因为情怀,js变得越来越让人无所适从则是因为赤裸裸的利益。

@coordcn C++复杂是因为情怀… js正在多元化的时候,不同的角度看来,它只能满足某些需求,也会因为需求的选择而最终趋于稳定。说赤裸裸的利益也没错,

回到顶部