function *foo(){ x++ yield //在这里暂停代码 console.log(x); } var x = 1 var it = foo() it.next() it.next()上面的代码就是一个生成器foo。声明的方式是*foo,这是为了与普通函数区分开来。
这就是生成器,是一类特殊的函数,可以一次或多次启动和暂停,并不一定非得要完成。
function *foo(x,y){ return x * y } var it = foo(6,7) var res = it.next() res.value //42
我们将foo(6,7)赋值给迭代器it,尽管看起来很熟悉,但是生成器foo并没有向普通函数一样实际运行。然后调用it.next(),指示生成器foo从当前位置开始继续运行,停在下一个yield处或者直到生成器结束。
function *foo(x){ var y = x * (yield 'hello') //yield的第一个值 return y } var it = foo(6) var res = it.next() //第一个next不传入任何东西 res.value //'hello' res = it.next(7) //向等待的yield传入7 res.value //42消息的传递是双向的,yield..作为一个表达式可以发出消息响应next()调用,next()调用也可以向暂停的yield表达式发送值。
第二个res = it.next(7)会将7传给暂停的yield,此时生成器运行到了最后,因此res的值实际上就是return的值
function *foo(x){ var x = yield 2 z++ var y = yield(x * z) console.log(x,y,z); } var z = 1 var it1 = foo() var it2 = foo() var val1 = it1.next().value //2,yield 2 var val2 = it2.next().value //2,yield 2 val1 = it1.next(val2 * 10).value //40,x:20;z:2 val1 = it1.next(val1 * 5).value //600,x:200;z:3 it1.next(val2 / 2) //y:300;20 322 3 it2.next(val1 / 4) //y:10;200 10 3迭代器仍然是函数,每一个迭代器内部的值不会互相干扰
function request(url){ return new Promise((resolve, reject) => { ajax(url,resolve) }) } function foo(x,y){ return request( `http://some.url.1?x=${x}&y=${y}` ) } foo(11,31) .then( function(text){ console.log(text); }, function(err){ console.log(err); } )
这里支持Promise的foo函数在发出Ajax调用后返回了一个Promise,这暗示我们可以通过foo构造一个Promise,并且通过生成器把它yield出来,然后通过迭代器控制代码就可以接受到这个Promise。
但是迭代器应该对这个Promise做些什么呢?
function request(url){ return new Promise((resolve, reject) => { ajax(url,resolve) }) } function foo(x,y){ return request( `http://some.url.1?x=${x}&y=${y}` ) } function *main(){ try { var text = yield foo(11,13) } catch (error) { console.error(err) } } var it = main() var p = it.next().value // 等待Promise决议 p.then( function(text){ it.next(text) }, function(err){ it.throw(err) } )我们发现,*main生成器中的代码完全不需要改变,在生成器内部,不管什么值yield出来,都只是一个透明的细节,我们甚至没有意识到其发生。
我们需要关注的额,是怎么运行*main,首先通过var it = main()开启生成器,再通过var p = it.next().value获得foo返回的Promise并赋值给p,此时的p还是未决议状态,一旦Promise完成了,就会调用it.next()或者it.throw()执行生成器下一步。
当然,我们不可能为每一个生成器都编写不同的Promise链,如果有一个工具为我们实现的话该会有多好呀!