Generator 的具体应用 Iterator 遍历器 Thunk 函数 Generator 的具体应用 前面用两节的内容介绍了Generator可以让执行处于暂停状态,并且知道了Generator返回的是一个Iterator对象,这一节就详细介绍一下Generator的一些基本用法。 本节内容概述 next和yield参数传递 for...of的应用示例 yield*语句 Generator中的this 接下来... next和yield参数传递 我们之前已经知道,yield具有返回数据的功能,如下代码。yield后面的数据被返回,存放到返回结果中的value属性中。这算是一个方向的参数传递。 function* G() { yield 100 } const g = G() console.log( g.next() ) // {value: 100, done: false} 还有另外一个方向的参数传递,就是next向yield传递,如下代码。 function* G() { const a = yield 100 console.log('a', a) // a aaa const b = yield 200 console.log('b', b) // b bbb const c = yield 300 console.log('c', c) // c ccc } const g = G() g.next() // value: 100, done: false g.next('aaa') // value: 200, done: false g.next('bbb') // value: 300, done: false g.next('ccc') // value: undefined, done: true 捋一捋上面代码的执行过程: 执行第一个g.next()时,为传递任何参数,返回的{value: 100, done: false},这个应该没有疑问 执行第二个g.next('aaa')时,传递的参数是'aaa',这个'aaa'就会被赋值到G内部的a标量中,然后执行console.log('a', a)打印出来,最后返回{value: 200, done: false} 执行第三个、第四个时,道理都是完全一样的,大家自己捋一捋。 有一个要点需要注意,就g.next('aaa')是将'aaa'传递给上一个已经执行完了的yield语句前面的变量,而不是即将执行的yield前面的变量。这句话要能看明白,看不明白就说明刚才的代码你还没看懂,继续看。 for...of的应用示例 针对for...of在Iterator对象的操作之前已经介绍过了,不过这里用一个非常好的例子来展示一下。用简单几行代码实现斐波那契数列。通过之前学过的Generator知识,应该不难解读这份代码。 function* fibonacci() { let [prev, curr] = [0, 1] for (;;) { [prev, curr] = [curr, prev + curr] // 将中间值通过 yield 返回,并且保留函数执行的状态,因此可以非常简单的实现 fibonacci yield curr } } for (let n of fibonacci()) { if (n > 1000) { break } console.log(n) } yield*语句 如果有两个Generator,想要在第一个中包含第二个,如下需求: function* G1() { yield 'a' yield 'b' } function* G2() { yield 'x' yield 'y' } 针对以上两个Generator,我的需求是:一次输出a x y b,该如何做?有同学看到这里想起了刚刚学到的for..of可以实现————不错,确实可以实现(大家也可以想想到底该如何实现) 但是,这要演示一个更加简洁的方式yield*表达式 function* G1() { yield 'a' yield* G2() // 使用 yield* 执行 G2() yield 'b' } function* G2() { yield 'x' yield 'y' } for (let item of G1()) { console.log(item) } 之前学过的yield后面会接一个普通的 JS 对象,而yield*后面会接一个Generator,而且会把它其中的yield按照规则来一步一步执行。如果有多个Generator串联使用的话(例如Koa源码中),用yield*来操作非常方便。 Generator中的this 对于以下这种写法,大家可能会和构造函数创建对象的写法产生混淆,这里一定要注意 —— Generator 不是函数,更不是构造函数 function* G() {} const g = G() 而以下这种写法,更加不会成功。只有构造函数才会这么用,构造函数返回的是this,而Generator返回的是一个Iterator对象。完全是两码事,千万不要搞混了。 function* G() { this.a = 10 } const g = G() console.log(g.a) // 报错 接下来... 本节基本介绍了Generator的最常见的用法,但是还是没有和咱们的最终目的————异步操作————沾上关系,而且现在看来有点八竿子打不着的关系。但是话说回来,这几节内容,你也学到了不少知识啊。 别急哈,即便是下一节,它们还不会有联系,再下一节就真相大白了。下一节我们又给出一个新概念————Thunk函数 Iterator 遍历器 Thunk 函数