generator
是ES6新的语法,我们先简单的回顾一下他的使用:
generator基本用法
1 | function * genFn() { |
generator
函数是在function
和函数名之间添加*
来定义的。generator
函数调用后并没有真正的执行,当调用返回对象的next
方法会执行并返回generator
函数定义处的yield
(或return
)前面的值,从而使得执行与定义分离。
generator
对象的next
方法也可以传参:
1 | function * genFn(arg) { |
generator简单异步处理
现在使用有一个异步的fetch
请求,打印出它返回的结果,我们可以这么写generator
:
1 | function* asyncGenFn() { |
上面我们已经把一个异步操作用generator处理了,我们现在处理2个异步操作,再加一个fetch请求发送后的1秒后打印字符串的一个异步操作。
1 | var promise = new Promise(function(resolve, reject) { |
通过2次的异步请求我们貌似发现了点处理规律,上面对generator
的处理基本上都是大同小异,唯一一点区别就是result1.value.then
调用的时候先转换了一下数据。其实转换数据这一个步骤也是一个Promise
那我们就可以把他当做异步来处理咯,也就是可以放在asyncGenFn
函数内部来处理,请看这里:
1 | function* asyncGenFn() { |
这下有没有豁然开朗,异步操作的执行其实是一个套路,就是递归调用gen.next().value.then()
就可以了。由上可知,异步的generator
执行时如果遇到yield
那么就去调用gen.next().value.then()
去处理该Promise
,后面这个处理的过程是很机械地,我们是否可以把处理Promise
这个过程封装一下,然后将注意力完全放在generator
上呢,假如我们把这个封装好的东西叫他执行器,那样我们就彻底不需要关注执行器怎么实现了,只需要关注generator
然后用执行器去执行它。此时你貌似懂了点什么,但是你还会问如果不是Promise
的异步操作呢?我们先不考虑这种情况,这里假设你很聪明,传的所有的异步操作都是Promise
。某大神说:“过早的优化是万恶之源。”。
现在我们就简单的实现一下这个执行器吧:
1 | function actuator(gen){// 接收一个 generator 函数 |
generator异步处理绕不开的一个库co
我们用了很少的几行代码写了一个执行器,其实这上面的actuator
函数是对大神TJ Holowaychuk
所写的co库的拙劣模仿,现在我们可以直接引用co
库来实现我们的异步操作:
1 | var co = require('co'); |
处理generator
的异步就这么简单,直接用co
库包一层就会执行。之前我们还留了一个问题,如果yield
后面的不是Promise
那该怎么办?其实也好办,只要把它转换为Promise
就可以了,co
也是这么做的。另外我们这里假设都是直接成功的,失败的情况下并没有考虑,co
已经把失败的情况也处理了。那它在我们的actuator
函数的基础上做了那些操作呢?请看co源码,github仓库在这里。
async函数处理异步
async函数处理异步也很简单,如上面的例子我们可以这么写:
1 | async function asyncFn() {// 使用async关键字的函数 |
不知道你有没有发现async
函数和generator
函数处理异步的代码很相似,无非就是把*
换成async
并且挪了一个位置(不挪位置编译器还以为async
是你的函数名呢),然后把里面的yeild
换成了await
。其实async
函就是generator
和co
的语法糖:
1 | async function fn(args) { |