从ES6生成器(Generator)原理解读,到理解ES7的asyn...await...
ES6生成器原理解读
生成器从本质上来说,是一种特殊的迭代器。为什么这么说呢,下面来看一段代码:
Example
function* gen(arg){
yield 2;
yield arg;
}
let genHandle = gen(3);
for(let i of genHandle){
console.log(i); // 依次打印:2,3
}
let genHandle2 = gen(4);
console.log(genHandle2.next()); // { value: 2, done: false }
console.log(genHandle2.next()); // { value: 4, done: false }
console.log(genHandle2.next()); // { value: undefined, done: true }
代码解读:从上面代码可以看出生成器其实是一个变异的函数,和一般的函数没什么不同,只是多了一个 * 来区分这是一个生成器。当然生成器内部多了一个yeild语句,作用显而易见是为了停止继续执行下面的代码,相当于return的作用一样,但是不同的是它可以保存进度,可以通过代码控制继续执行。而且这里的yeild一定要在生成器的作用域范围内才会有效,这点要注意。在执行完生成器返回的对象genHandle和genHandle2可以看出,它们都是迭代器,也就是说生成器执行结果返回的是迭代器对象,即生成器可以看成是一个迭代器的构造器,可以用来构建生成迭代器。yield在生成器构建迭代器的过成中的作用中就类似于切割代码的作用,把代码切割成一个可以迭代的集合。下面是上面的gen生成器转成ES5的代码,如下:
Example
function gen(arg) {
return regeneratorRuntime.wrap(function gen$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return 2;
case 2:
_context.next = 4;
return arg;
case 4:
case "end":
return _context.stop();
}
}
}, _marked[0], this);
}
Example:为了便于理解,下面是我实现的一个简单的regeneratorRuntime.wrap函数来生成迭代器:
var context = {
next:0,
prev:null,
gen$:null,
done:false,
stop:function(){
this.done = true;
}
};
Object.defineProperty(context, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function () {
var me = this;
return {
next: function () {
var nextValue = me.gen$(me);
return {
value: nextValue,
done: me.done
}
}
}
}
});
var regeneratorRuntime = {
wrap:function(_gen){
context.gen$ = _gen;
return context;
}
}
function gen(arg) {
return regeneratorRuntime.wrap(function gen$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return 2;
case 2:
_context.next = 4;
return arg;
case 4:
case "end":
return _context.stop();
}
}
});
}
var b = gen(3);
for(var c of b){
console.log(c); // 结果为:2,3
}
理解ES7的 asyn...await...
Example
// 正常异步执行
function demo(){
new Promise(function(resolve,reject){
resolve(3)
}).then((value)=>{
console.log(value)
})
console.log(4)
}
demo(); // 执行结果依次是:4,3
// 生成器异步转同步
let gen = null;
function* genDemo(){
yield setTimeout(()=>{
console.log(3);
gen.next();
},100)
console.log(4)
}
gen = genDemo();
gen.next() // // 执行结果依次是:3,4
// async...await...异步转同步
async function asyncDemo(){
await new Promise(function(resolve,reject){
resolve(3)
}).then((value)=>{
console.log(value)
})
console.log(4)
}
asyncDemo(); // 执行结果依次是:3,4
代码解读:从上述的例子可以看出,生成器是可以解决异步如果转成同步代码的问题,而async...await...的实现原理其实也是基于生成器来实现的,不过这里要注意的是async...await...必须要配合Promise来实现,因为Promise的决议回调函数里面集成了类似于生成器gen.next()这样的代码来控制继续执行代码。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。