JS中你所不知道的的Generator函数(上)

ES6提供了一种新型的异步编程解决方案:Generator函数(以下简称G函数)。它不是使用JS现有能力按照一定标准制定出来的东西(Promise是如此出生的),而是具有新型底层操作能力,与传统编程完全不同,代表一种新编程逻辑的高大存在。简洁方便、受人喜爱的async函数就是以它为基础实现的。

意义

JS引擎是单线程的,只有一个函数执行栈。

当当前函数执行完后,执行栈将其弹出,销毁包含其局部变量的栈空间,并开始执行前一个函数。执行权由此单向稳定的在不同函数中切换。虽然Web Worker的出现使我们能够自行创建多个线程,但这离灵活的控制:暂停执行、切换执行权和中间的数据交换等等,还是很有距离的。

G函数的意义在于,它可以在单线程的背景下,使执行权与数据自由的游走于多个执行栈之间,实现协程式编程。

调用G函数后,引擎会为其开辟一个独立的函数执行栈(以下简称G栈)。在执行它的过程中,可以控制暂停执行,并将执行权转出给主执行栈或另一个G栈(栈在这里可理解为函数)。而此G栈不会被销毁而是被冻结,当执行权再次回来时,会在与上次退出时完全相同的条件下继续执行。

下面是一个简单的交出和再次获得执行权的例子。

JS中你所不知道的的Generator函数(上)

登堂

形式

G函数也是函数,所以具有普通函数该有的性质,不过形式上有两点不同。一是在function关键字和函数名之间有一个*号,表示此为G函数。二是只有在G函数里才能使用yield命令(以及yield*命令),处于其内部的非G函数也不行。由于箭头函数不能使用yield命令,因此不能用作于Generator函数(可以用作于async函数)。

以下是它的几种定义方式。

JS中你所不知道的的Generator函数(上)

执行

调用普通函数会直接执行函数体中的代码,之后返回函数的返回值。但G函数不同,执行它会返回一个遍历器对象(此对象与数组中的遍历器对象相同),不会执行函数体内的代码。只有当调用它的next方法(也可能是其它实例方法)时,才开始了真正执行。

在G函数的执行过程中,碰到yield或return命令时会停止执行并将执行权返回。当然,执行到此函数末尾时自然会返回执行权。每次返回执行权之后再次调用它的next方法(也可能是其它实例方法),会重新获得执行权,并从上次停止的地方继续执行,直到下一个停止点或结束。

JS中你所不知道的的Generator函数(上)

JS中你所不知道的的Generator函数(上)

入室

数据交互

数据如果不能在执行权的更替中取得交互,其存在的意义就会大打折扣。

G函数的数据输出和输入是通过yield命令和next方法实现的。

yield和return一样,后面可以跟上任意数据,程序执行到此会交出控制权并返回其后的跟随值(没有则为undefined),作为数据的输出。每次调用next方法将控制权移交给G函数时,可以传入任意数据,该数据会等同替换G函数内部相应的yield xxx表达式,作为数据的输入。

执行G函数,返回的是一个遍历器对象。每次调用它的next方法,会得到一个具有value和done字段的对象。value存储了移出控制权时输出的数据(即yield或return后的跟随值),done为布尔值代表该G函数是否已经完成执行。作为遍历器对象的它具有和数组遍历器相同的其它性质。

JS中你所不知道的的Generator函数(上)

实际上,G函数是实现遍历器接口最简单的途径,不过有两点需要注意。一是G函数中的return语句,虽然通过遍历器对象可以获得return后面的返回值,但此时done属性已为true,通过for of循环是遍历不到的。二是G函数可以写成为永动机的形式,类似服务器监听并执行请求,这时通过for of遍历是没有尽头的。

示例一:return 返回值。

JS中你所不知道的的Generator函数(上)

示例二:作为遍历器接口。

JS中你所不知道的的Generator函数(上)

示例三:永动机。

JS中你所不知道的的Generator函数(上)

编程是一种修行,我愿与志同道合的朋友携手前行,一起探索有关编程的奥妙!

如果您在前端学习的过程中遇到难题,欢迎【关注】并【私信】我,大家一起交流解决!

文章推荐:


分享到:


相關文章: