我也来凑个热闹,看看编译后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实现,拭目以待吧
如有不对的地方,请指正
跟楼上一些观点一样,我也觉得 babel 的结果跟 V8 最终的实现不一定是一致的。babel 受限于目前 v8 的能力,无论用了 promise 还是 yield 都只能看做是一种对现实的妥协。
最终 async/await 的性能,以及与 generator 的差异,还是要看 v8 的实现。
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.
举个例子,Chakra: 如果翻了Chakra的代码,就会发现yield和await在token解析时大多相邻甚至写在一起,最后到下面那个函数都以类型Js::OpCode::Yield进行处理。所以整块Chakra中,出现await的基本只有Parser部分。 (https://github.com/Microsoft/ChakraCore/blob/3254c38a313f4adfff87c887837ed870fd9e5023/lib/Runtime/ByteCode/ByteCodeEmitter.cpp#L11010)
(非专业,如有错误,敬请指出)
本来就是语义层面的东西。。。为啥要下降到实现里讨论呢。。。先前yield编译还变成一堆switch呢,但是这有什么影响吗? await这种东西一看就明白了,yield看了之后还要考虑是不是有别的用法。除了常见的迭代器,之前我还用它做过一个有限状态机的核心部分,这种情况下yield就不能当作await用了。
来自酷炫的 CNodeMD