function *createNumberIterator() { yield 1; yield 2; } function *createColorIterator() { yield "red"; yield "green"; } function *createCombinedIterator() { yield *createNumberIterator(); yield *createColorIterator(); yield true; } var iterator = createCombinedIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: "red", done: false }" console.log(iterator.next()); // "{ value: "green", done: false }" console.log(iterator.next()); // "{ value: true, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
这里的生成器createCombinedIterator()先后委托了另外两个生成器createNumberlterator()和createColorlterator()。仅根据迭代器的返回值来看,它就像是一个完整的迭代器,可以生成所有的值。每一次调用next()方法就会委托相应的迭代器生成相应的值,直到最后由createNumberlterator()和cpeateColorlterator()创建的迭代器无法返回更多的值,此时执行最后一条yield语句并返回true
有了生成器委托这个新功能,可以进一步利用生成器的返回值来处理复杂任务
function *createNumberIterator() { yield 1; yield 2; return 3; } function *createRepeatingIterator(count) { for (let i=0; i < count; i++) { yield "repeat"; } } function *createCombinedIterator() { let result = yield *createNumberIterator(); yield *createRepeatingIterator(result); } var iterator = createCombinedIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
在生成器createCombinedlterator()中,执行过程先被委托给了生成器createNumberlterator(),返回值会被赋值给变量result,执行到return 3时会返回数值3。这个值随后被传入createRepeatinglterator()作为它的参数,因而生成字符串"repeat"的yield语句会被执行三次
无论通过何种方式调用迭代器next()方法,数值3都不会被返回,它只存在于生成器createCombinedlterator()的内部。但如果想输出这个值,则可以额外添加一条yield语句
function *createNumberIterator() { yield 1; yield 2; return 3; } function *createRepeatingIterator(count) { for (let i=0; i < count; i++) { yield "repeat"; } } function *createCombinedIterator() { let result = yield *createNumberIterator(); yield result; yield *createRepeatingIterator(result); } var iterator = createCombinedIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
此处新添加的yield语句显式地输出了生成器createNumberlterator()的返回值。
[注意]yield*也可直接应用于字符串,例如yield* "hello",此时将使用字符串的默认迭代器
异步任务执行
生成器令人兴奋的特性多与异步编程有关,JS中的异步编程有利有弊:简单任务的异步化非常容易;而复杂任务的异步化会带来很多管理代码的挑战。由于生成器支持在函数中暂停代码执行,因而可以深入挖掘异步处理的更多用法
执行异步操作的传统方式一般是调用一个函数并执行相应回调函数
let fs = require("fs"); fs.readFile("config.json", function(err, contents) { if (err) { throw err; } doSomethingWith(contents); console.log("Done"); });
调用fs.readFile()方法时要求传入要读取的文件名和一个回调函数,操作结束后会调用该回调函数并检查是否存在错误,如果没有就可以处理返回的内容。如果要执行的任务很少,那么这样的方式可以很好地完成任务;如若需要嵌套回调或序列化一系列的异步操作,事情会变得非常复杂。此时,生成器和yield语句就派上用场了
【简单任务执行器】
由于执行yield语句会暂停当前函数的执行过程并等待下一次调用next()方法,因此可以创建一个函数,在函数中调用生成器生成相应的迭代器,从而在不用回调函数的基础上实现异步调用next()方法