iterable vs. iterator
generator function每次调用都会产生一个新的iterator实例,该iterator同时也是iterable,含有Symbol.iterator函数,基本上这个函数就是return this
generator 控制异步回调
在generator中yield一个异步操作,在该异步操作的回调中重启该generator. 在生成器内部我们实现了“用同步形式来调用异步操作”,而回调细节被封装隐藏起来。
function foo(url){ajax(ur, (err,data)=>{if(err){it.throw(err)}else{it.next(data)}})}function *main(){try {const text = yield foo('http://example.com')console.log(text)}catch(err){console.error(err)}}const it = main();it.next()
这里yield返回的是undefined, 并没有给外界传递消息。注意,使用生成器可以达到“同步顺序”的目的,但失去了promise的可信任可组合功能。
ES6中最完美的世界就是生成器(看似同步的异步代码)和 Promise(可信任可组合)的结合
generator + Promise
将异步操作ajax用promise封装,并用yield返回这个promise. 这样我们就能很好地利用promise的功能. 与上面的代码相比,这里的generator是通过promise决议来重启的。
function foo(url){return request(url) // request返回一个promise}function *main(){//与上面一样,无需修改}const it = main()const p = it.next()//等待promise决议p.then(function(text){it.next(text)}, function (err) {it.throw(err)})
前面的generator *main()都只有一个异步操作,如果有多个异步操作,手动驱动(启动/重启)该generator 会相当麻烦,最好是使用专门设计的工具(ES6未提供,很多Promise库提供)。以下为一个示例:
//Benjamin Gruebaum(@benjamingr on GitHub)function run(gen){const args = [].slice.call(arguments, 1)const it = gen.apply(this, args)return Promise.resolve().then(funciton handleNext(value){const next = it.next(value)return (function handleResult(next){ //此次封装是为了将该函数实名化,以备后面调用if(next.done){return next.value;}else{return Promise.resolve(next.value).then(handleNext, function handleError(err){return Promise.resolve(it.throw(err)).then(handleResult) // 注意!})})(next);}})}
ES8中的async/await
async function总是返回一个promise, await 接受一个promise (thenable) 并(异步地)等待它返回。如果你await了一个Promise, async函数就会自动获知要做什么,它会暂停这个函数(就像生成器一样),直到promise决议。
生成器委托
yield *可以委托到任意的iterator
Promise vs. thunk
Promisify 与 thunkify 类似,本质上都是发出一个请求,并分别由返回的promise和thunk表示对这个请求的未来的答复。生成器中yield也可授受一个thunk, 只需要一个更智能的run()工具。thunk可看作promise的简化版,没有可信任和可组合性。
generator手工实现
generator可以通过函数闭包与状态机来实现:
- 函数闭包可以保持多次调用的上下文
- 状态机模拟每次调用的流程控制
考虑如下一个简单的generator
function* foo(url) {try {console.log("requesting:", url)let val = yield request(url)console.log(val)} catch (err) {console.log("Oops:", err)return false}}
可以通过以下代码来手工实现,该实现可用于ES6之前,也展示了generator的实现原理。
function foo(url) {let statelet valfunction process(v) {switch (state) {case 1:console.log("requesting:", url)return request(url)case 2:val = vconsole.log(val)return;case 3:let err = v;console.log("Oops:", err)return false}}//构造并返回一个iteratorreturn {next: function (v) {if (!state) {state = 1return {done: falsevalue: process()}} else if (state == 1) {state = 2return {done: truevalue: process(v)}} else {return {done: truevalue: undefined}}},throw: function (e) {if(state==1){state = 3return {done: truevalue: process(e)}}else{throw e}}}}
