除了给迭代器传递数据外,还可以给它传递错误条件。通过throw()方法,当迭代器恢复执行时可令其抛出一个错误。这种主动抛出错误的能力对于异步编程而言至关重要,也能提供模拟结束函数执行的两种方法(返回值或抛出错误),从而增强生成器内部的编程弹性。将错误对象传给throw()方法后,在迭代器继续执行时其会被抛出
function *createIterator() { let first = yield 1; let second = yield first + 2; // yield 4 + 2 ,然后抛出错误 yield second + 3; // 永不会被执行 } let iterator = createIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next(4)); // "{ value: 6, done: false }" console.log(iterator.throw(new Error("Boom"))); // 从生成器中抛出了错误
在这个示例中,前两个表达式正常求值,而调用throw()方法后,在继续执行let second求值前,错误就会被抛出并阻止了代码继续执行。这个过程与直接抛出错误很相似,二者唯一的区别是抛出的时机不同
可以在生成器内部通过try-catch代码块来捕获这些错误
function *createIterator() { let first = yield 1; let second; try { second = yield first + 2; // yield 4 + 2 ,然后抛出错误 } catch (ex) { second = 6; // 当出错时,给变量另外赋值 } yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next(4)); // "{ value: 6, done: false }" console.log(iterator.throw(new Error("Boom"))); // "{ value: 9, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
在此示例中,try-catch代码块包裹着第二条yield语句。尽管这条语句本身没有错误,但在给变量second赋值前还是会主动抛出错误,catch代码块捕获错误后将second变量赋值为6,下一条yield语句继续执行后返回9
这里有一个有趣的现象调用throw()方法后也会像调用next()方法一样返回一个结果对象。由于在生成器内部捕获了这个错误,因而会继续执行下一条yield语句,最终返回数值9
如此一来,next()和throw()就像是迭代器的两条指令,调用next()方法命令迭代器继续执行(可能提供一个值),调用throw()方法也会命令迭代器继续执行,但同时也抛出一个错误,在此之后的执行过程取决于生成器内部的代码
在迭代器内部,如果使用了yield语句,则可以通过next()方法和throw()方法控制执行过程,当然,也可以使用return语句返回一些与普通函数返回语句不太一样的内容
【生成器返回语句】
由于生成器也是函数,因此可以通过return语句提前退出函数执行,对于最后一次next()方法调用,可以主动为其指定一个返回值。正如在其他函数中那样,可以通过return语句指定一个返回值。而在生成器中,return表示所有操作已经完成,属性done被设置为true;如果同时提供了相应的值,则属性value会被设置为这个值
function *createIterator() { yield 1; return; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
这段代码中的生成器包含多条yield语句和一条return语句,其中return语句紧随第一条yield语句,其后的yield语句将不会被执行
在return语句中也可以指定一个返回值,该值将被赋值给返回对象的value属性
function *createIterator() { yield 1; return 42; } let iterator = createIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 42, done: true }" console.log(iterator.next()); // "{ value: undefined, done: true }"
在此示例中,第二次调用next()方法时返回对象的value属性值为42,done属性首次设为true;第三次调用next()方法依然返回一个对象,只是value属性的值会变为undefined。因此,通过return语句指定的返回值,只会在返回对象中出现一次,在后续调用返回的对象中,value属性会被重置为undefined
[注意]展开运算符与for-of循环语句会直接忽略通过return语句指定的任何返回值,只要done一变为true就立即停止读取其他的值。不管怎样,迭代器的返回值依然是一个非常有用的特性
【委托生成器】
在某些情况下,我们需要将两个迭代器合二为一,这时可以创建一个生成器,再给yield语句添加一个星号,就可以将生成数据的过程委托给其他生成器。当定义这些生成器时,只需将星号放置在关键字yield和生成器的函数名之间即可