一、生成器函数
类似于python,js也有自己的生成器,其基本的使用和定义方式如下所示:
1 | function* myGenertor(){ |
最后,这个程序依次输出的是test1.test2和test3,这也是JavaScript的一个生成器。在定义函数的时候在function后面加一个*表示这是一个生成器函数,并通过关键字yield返回相应的值。并且,对于生成器函数来说,它不会像普通函数一样停止执行,而是当一个值的请求到来后,生成器就会从上次离开的地方继续开始执行。示例如下:
1 | function* myGenertor(){ |
从上面这个例子可知,第一次调用myGenenrtor这个生成器函数的时候,首先定义了一个名为bb的变量并且初始化为1,然后将这个值返回。第二次调用的时候,生成器不会像普通函数那样,再次从函数的开始执行,而是从上次执行的语句之后又开始执行b++,并返回此时bb的值为2.然后继续这个过程,返回的值是3.在这里,for-of循环就是对迭代器进行迭代的语法糖。
二、通过迭代对象控制生成器
首先看下面这个例子
1 | function* myGenertor(){ |
在上面这个例子中,依然定义了一个生成器函数myGenertor,接下来调用这个生成器函数,但这一次赋给了一个名为myIterator的一个变量,如果直接输出这个变量,得到的是一个空的对象,只有对这个对象执行接口方法next(),生成器才开始执行代码,当代码执行到yield关键字的时候,生成一个中间结果并且返回一个新的对象,其中封装了一个结果值和指示完成的指示器。每当生成器生成一个值之后,生成器就会非阻塞挂起,等待下一次请求的到达。当没有可执行的代码的时候,生成器就会返回一个value为“undefined”,done为true的值,表示他的状态已完成。
三、利用生成器函数把执行权交给下一个生成器
1 | function* myGenerator(action){ |
上面这个实例中,在迭代器上使用yield*操作符,程序会跳转到另一个生成器上执行,这一切对于最初调用的迭代器而言都是透明的。
四、迭代器与生成器之间的交互
现在来讨论以下迭代器与生成器的交互问题,从而更好的理解生成器与迭代器之间的关系。首先先来看下面这段代码:
1 | function* myGenerator(action){ |
这里,我们给生成器一个参数action。首先新建一个迭代器并赋给test这样的一个变量。并且这个时候,我们已经给myGenerator赋予了一个参数,所以在下一次调用的时候,会输出”hello”加上这个参数。之后,生成器就在yield这里被挂起,imposter始终都没有被赋值直到下一次再次调用next()的时候,此时imposter被赋予了next中的值,这里是因为yield的返回值就是下一次调用next()的参数,所以imposter就被赋值。这也是为什么不能通过第一次调用next()向生成器提供参数的原因,但还是可以在构造的时候提供初值。(构造的时候仅仅是给参数赋值,但生成器函数是不会运行的)
五、生成器异常和处理
1 | function* myGenerator(data){ |
这里将生成器中全部的代码装入try-catch块中去。在创建了迭代器之后,通过调用迭代器中的throw方法抛出异常,并利用生成器函数中的catch语句进行异常的接受和处理。
六、promise和生成器解决异步问题
首先测试下面的这段代码:
1 | function delay(time){ |
由于在生成器中存在异步操作,所以在使用next操作的时候,会存在异步问题。这时候检查第一个a.next()的返回值的时候,可以看到返回以下的结果:
1 | { value: Promise { <pending> }, done: false } |
说明,对于一个promise
,它的then()
函数的返回值同样是一个promise
对象。利用这个特性,将下次想要顺序执行的同步操作中的值放在promise函数的then之后,就可以得到我们想要的结果。
1 | const a = test() |
可以发现,如果在遇到异步请求的时候,就使用.then(),如果遇到同步操作,就直接next()即可,所以可将上述代码整理成为下面的方法,利用递归来实现这个过程:
1 | function himmel(gen) { |
最终,执行的结果就是按照顺序执行的1,2,3,4
###番外
异步问题的另一种解决方案——利用闭包来实现:
首先还是先看下面这个异步的例子:
1 | function delay(time){ |
上面的代码是一个典型的异步的问题,由于delay()是一个异步操作,所以在程序运行到delay的时候,将这个操作放在异步事件的队列中,先去进行同步操作,于是本该在异步操作执行结束之后执行的语句被优先执行了。并且程序在异步操作还没来得及开始就宣布结束了。并且之后,由于全局变量中的i已经增加到了5,所以最后得到的都是5,这显然不是我们预期的结果。首先解决i的问题,这里需要加一个闭包,将每次执行的i纪录下来,并且利用递归,在每次异步操作回调之后在递归下一次的操作,直到达到程序的终点为止。这样也就完成了程序从异步变为同步的操作。具体代码如下所示:
1 | function delay(time){ |