Generator是ES6一个颇有意思的特性,也是不容易理解的特性。不一样于let/const
提供了块级做用域这样明显的目的,这玩意儿被搞出来究竟是干吗的?javascript
首先咱们须要明确一个问题,在JavaScript中,任何一个函数只要开始执行,便没法中止下来直到执行完成(别跟我提debug,你见过哪一个用户在使用产品的还得开个debug调试你的代码)。java
but,Generator提供这种能力。 看下面代码:网络
function *g(){ console.log('start'); yield 1; console.log('middle'); yield 2; console.log('end'); } var g1 = g(); console.log(g1.next()); // start // {value: 1, done: false} console.log(g1.next()); // middle // {value: 2, done: false} console.log(g1.next()); // end // {value: undefined, done: true}
根据输出结果,咱们看到,在函数g
中,碰到yield
关键词,运行的程序会停下来。只有调用 next()
方法,才会继续执行函数g
中的代码。因此函数g
自己有暂停状态。app
至此,咱们须要知道:框架
既然Generator能够函数停下来,有些脑洞清奇的人,想到了可不能够用Generator处理异步程序。异步
先看一个传统例子:async
function asyn(fn) { return new Promise((resolve,reject)=>{ setTimeout(()=>{ fn(); resolve(true); }, 1000); }); } function main() { console.log('start'); asyn(function(d) { console.log('async one'); asyn(function(d) { console.log('async two'); console.log('end'); }); }); } main();
再来看看使用了Generator的异步程序:函数
function asyn(fn) { return new Promise((resolve,reject)=>{ setTimeout(()=>{ fn(); resolve(true); }, 1000); }); } co(function*() { console.log('start'); yield asyn(function(d) { console.log('async one'); }); yield asyn(function(d) { console.log('async two'); }); console.log('end'); }); function co(fn) { return new Promise((resolve,reject)=>{ let g = fn(); onFullfilled(); function onFullfilled() { let ret = null; ret = g.next(); next(ret); } function next(ret) { if(ret.done) return resolve(ret.value); ret.value.then(onFullfilled); } } ); }
函数在异步程序没有采用嵌套回调,是直接用同步的方式写了出来。道理大概就是,有两个异步程序,用小明和小红指代它们。小红须要等小明执行完了才能够被执行,那么咱们在执行到小明时,暂停程序的运行(经过yield
),等到小明有了返回结果时,再执行后面跟着的小红(next()
)。debug
在上面的程序中,咱们添加了一个co
函数,这个函数的做用是让Generator自动执行下去。直白来讲,就是当第一个异步函数返回后,自动调用next()
方法运行后面的代码。调试
Koa是基于Node.js的Web应用框架。在Koa中,处理的异步程序主要是网络请求(HTTP)、文件读取和数据查询。这里面的异步场景较多,若是再加上程序分层,采用传统的callback方式,那回调多了去了。
app.on('get', function(){ auth(function(){ router(function(){ find(function(){ save(function(){ render(function(){ //...... }) }) }) }) }) })
这样写法对于程序维护及其不利,毫无便捷性可言。在有了generator后,咱们即可以像上面那种方式来写程序。Koa最初的版本就是经过这种方式,让中间处理程序都转成一个个"yield"(中间件)。经过中间件的形式去处理客户端请求,让开发App应用更加灵活,不受框架自身限制。
在最新的Koa2中,已经抛弃了Genetator,转而使用async/await
。
可是不管采用哪一种方式,其本质都是利用了Promise。