---------------------------------------------------------------------------------------------------------------------------- 基本概念 Generator 函数是一个状态机,封装了多个内部状态,执行 Generator 函数会返回一个遍历器对象 Generator 函数除了状态机,还是一个遍历器对象生成函数, 返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态 function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); 该函数存在三个状态了 ---------------------------------------------------------------------------------------------------------------------------- 调用 Generator 函数后,该函数并不执行 返回的也不是函数运行结果,而是一个指向内部状态的指针对象(遍历器对象) 下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态 返回的对象是yield表达式的值以及遍历是否完成 (遇到下一个yield或者return为止) hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: 'ending', done: true } hw.next() // { value: undefined, done: true } ---------------------------------------------------------------------------------------------------------------------------- yield 表达式 遍历器对象的next方法的运行逻辑如下 (1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。 (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。 (3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止, 并将return语句后面的表达式的值,作为返回的对象的value属性值。 (4)如果该函数没有return语句,则返回的对象的value属性值为undefined。 注意:yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行(如果没有执行到这一步,就永远都不会执行该表达式) (因此相等于手动提供了一个“惰性求值”的语法功能) (5)yield不能进行嵌套操作,并且yield必须在定义的function*()函数里面,在数组等其他的回调函数是不起作用 //(6) yiled 后面使用的返回必须是返回了一个Promise对象 重点:bluebirdjs 类库会将普通函数转化成返回Promise对象的函数 ---------------------------------------------------------------------------------------------------------------------------- next() 方法的参数 1:该参数会被当成上一个yield表达式的返回值 ---------------------------------------------------------------------------------------------------------------------------- for...of 循环 for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法 1:一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象,就是不包含最后的那个return ---------------------------------------------------------------------------------------------------------------------------- Generator.prototype.throw() Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获 ---------------------------------------------------------------------------------------------------------------------------- Generator.prototype.return() Generator 函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历 Generator 函数 注释:return与next的简单区别 1:console.log(g.return('foo')); // { value: "foo", done: true } 终结了所以是true,并且返回了指定的值 2:return没有参数的时候,返回{ value: undefined, done: true } 3:next没有参数的时候返回本次yield语句的返回值 4:return有参数的时候,覆盖本次yield语句的返回值,也就是说,返回{ value: 参数, done: true }; next有参数的时候,覆盖上次yield语句的返回值 ---------------------------------------------------------------------------------------------------------------------------- next()、throw()、return() 的共同点 next()、throw()、return()这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式 ---------------------------------------------------------------------------------------------------------------------------- yield* 表达式 如果在 Generator 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的 单纯的使用另一个Generator无效 yield* fn() 这样才会起效果 ---------------------------------------------------------------------------------------------------------------------------- 作为对象属性的 Generator 函数 ---------------------------------------------------------------------------------------------------------------------------- Generator 函数的this ---------------------------------------------------------------------------------------------------------------------------- Generator 与状态机 1:常用的状态机写法 var ticking = true; var clock = function() { if (ticking) console.log('Tick!'); else console.log('Tock!'); ticking = !ticking; } 上面代码的clock函数一共有两种状态(Tick和Tock),每运行一次,就改变一次状态: 2:Generator的写法 var clock = function*() { while (true) { console.log('Tick!'); yield; console.log('Tock!'); yield; } }; ----------------------------------------------------------------------------------------------------------------------------
Generator 函数的异步应用 已知的异步编程 { 回调函数 事件监听 发布/订阅 Promise对象 } 回调函数写法 fs.readFile('/etc/passwd', 'utf-8', function (err, data) { if (err) throw err; console.log(data); }); Promise写法 var readFile = require('fs-readfile-promise'); readFile(fileA) .then(function (data) { console.log(data.toString()); }) .then(function () { return readFile(fileB); }) .then(function (data) { console.log(data.toString()); }) .catch(function (err) { console.log(err); }); Generator写法 function* gen(x) { var y = yield x + 2; return y; } var g = gen(1); g.next() // { value: 3, done: false } g.next() // { value: undefined, done: true } 数据交换与错误解决 { next返回值的value属性,是Generator函数向外输出,next函数还可以接受参数向函数体输入数据 Generator函数体内部还可以部署错误处理代码,捕获函数体外抛出的错误 } 参数的求值策略 { 传值调用:在进入函数体之前进行调用 传名调用:将表达式传入函数体,在执行的时候才调用 } Thunk 函数替换的不是表达式,而是多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数 Generator函数的自动执行方法 { }