我们知道 JavaScript 函数是从上到下执行的,但 ES6 在2016年6月发布时,这一切都改变了,它带来了在执行过程中暂停函数的能力,又能从暂停处继续执行。
这是怎么做到的呢?Generator 也称为生成器函数。
官方的介绍生成器函数提供了一个强大的选择,它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用
function*
语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator
的迭代器。 通过调用生成器的下一个方法消耗值时,Generator
函数将执行,直到遇到yield
关键字。
生成器函数可以在中间暂停一次或多次,之后可以重新恢复。当标准函数被调用时,控制将由被调用函数控制,直到它返回为止,但是生成器函数允许调用函数控制被调用函数的执行。如下图示意:
生成器函数关键的三个概念:
function*
:用于将函数声明为生成器函数,比普通的函数声明多了一个*
,*
位置可以在function
关键字旁边,也可以在函数名旁边。yield
:只能出现在生成器函数中,但是生成器也可以没有yield
关键字,函数遇到yield
时会暂停并抛出它后面的表达式的结果。next
:将代码的控制权返回给生成器函数。
下面来看一个基本的实例。
创建生成器函数
JavaScript 中的生成器函数的创建与其他函数非常相似,除了语法上的一点差异,如下:
function* citySolitaire() { yield "北京市"; yield "上海市"; yield "深圳市"; yield "广州市"; yield "天津市"; }
区别在于函数定义本身,生成器函数是使用 function*
语法声明的。
yield 关键字
当一个生成器函数被调用时,它会一直执行直到遇到一个 yield
表达式。此时,生成器函数暂停,返回 yield
关键字后定义的值,等待继续执行,直到再次调用该函数。
返回值
JavaScript 中生成器函数和常规函数之间的另一个区别:生成器函数在封装的对象中返回其值。从技术上讲,生成器符合迭代器协议(如 Maps 和 Observables),这意味着封装的对象看起来像这样:
{ value, // 从生成器函数获取的下一个值 done; // 一个标志,标记是否是序列中的最后一个值 }
如前面定义的生成器函数 citySolitaire
,从中获取其值,如下:
const city = citySolitaire(); console.log(city.next()); // { value: '北京市', done: false }
每次调用 next()
方法时,都会以前面提到的格式返回一个对象。要获取数据,只需访问 value
属性。当 done
属性的值为 false
时,就意味着还有更多的数据需要检索,可以再次调用生成器。如下:
const city = citySolitaire(); console.log(city.next()); // { value: '北京市', done: false } console.log(city.next()); // { value: '上海市', done: false } console.log(city.next()); // { value: '深圳市', done: false } console.log(city.next()); // { value: '广州市', done: false } console.log(city.next()); // { value: '天津市', done: false } console.log(city.next()); // { value: undefined, done: true }
正如上面执行的结果,当执行到最后一个的时候再次执行 next()
,就会返回期望的 done
为 true
。
总结
JavaScript生成器函数是不很奇妙,通过本文可以简单了解一下什么是生成器函数,至于能够做什么?请听下回分解!