上一篇文章我们说了协程和线程的区别,这次我们来讲讲协程和生成器(Generator).
生成器(Generator)名字里之所以有生成(generate)二字,是因为它的作用:生成器是用来生成值(Value)的。它是一个会返回值的函数。
例如,我们想要一个函数,每次调用它,就会返回给我们一个Fibonacci数;第N次调用它,就返回给我们第N个Fibonacci数:
- let cur = 0
- let next = 1
- const fibo = () => {
- const p = cur + next
- cur = next
- next = p
- return cur
- }
用的时候我们有:
- const p1 = fibo()
- const p2 = fibo()
从使用角度看,这个函数每调用一次,就生成一个值,所以叫生成器。
在生成器里面,可以有一个yield关键字,用来代替我们上面的return:
- const fibo = () => {
- while (true) {
- yield(cur)
- let p = cur + next
- cur = next
- next = p
- }
- }
这个生成器每当执行到yield的时候,就会像return一样,先把cur的值返回;并且暂停执行;直到下一次被调用的时候,才会从yield的下一行继续执行。 在会挂起和继续执行的概念上,和协程是很相似的。
实际上,生成器和协程如此相似,以至于有人把它叫做semicoroutine,也就是半协程。
生成器和协程的区别主要是以下方面:
A generator looks like a function but behaves like an iterator
生成器看起来像一个函数,用起来像迭代器(或数组)
而协程主要是为了主动让出执行,进行协作式并发。
我们可以使用生成器来实现协程。
具体思路是:在生成器返回的时候,启动一个调度器(dispatcher)来明确指定下一个该运行的是谁。下面的例子来自Wiki:
- var q := new queue
- generator produce
- loop
- while q is not full
- create some new items
- add the items to q
- yield consume
- generator consume
- loop
- while q is not empty
- remove some items from q
- use the items
- yield produce
- subroutine dispatcher
- var d := new dictionary(generator → iterator)
- d[produce] := start produce
- d[consume] := start consume
- var current := produce
- loop
- current := next d[current]
可以看到,上面的生成器和调度器的配合,是通过函数名字进行的:
一些没有实现协程的语言会使用这种方法实现一个协程的库,比如Python2.5以前的版本。