Jsenb(js中Generator函数详解)
1. Generator的定义和执行
如果说 Promise 是为了解决回调地狱的难题出现的 ,那么 Generator 就是为了解决异步问题而出现的 。
普通函数 ,如果调用它会立即执行完毕;Generator 函数 ,它可以暂停 ,不一定马上把函数体中的所有代码执行完毕 ,正是因为有这样的特性 ,它可以用来解决异步问题 。
定义一个 Generator 函数 ,定义的方式和定义一个普通函数是类似的 ,不同之处在于它在 function 和函数名之间有一个*号 。
Generator 函数返回是一个迭代器对象 ,需要通过 xx.next 方法来完成代码执行 。在调用 generator 函数时 ,它只是进行实例化工作 ,它没有让函数体里面的代码执行 ,需要通过 next 方法来让它执行,比如像下面这样:
function* gen() { console.log(1) } // 定义迭代器对象 const iterator = gen() iterator.next() // 如果不执行这一局代码 ,1不会被打印当 next 方法执行时遇到了 yield 就会停止 ,直到你再次调用 next 方法 。比如像下面这样:
function* gen() { yield 1 console.log(A) yield 2 console.log(B) yield 3 console.log(C) return 4 } // 定义迭代器对象 const iterator = gen() iterator.next() // 执行 gen 函数,打印为空 ,遇到 yield 1 停止执行 iterator.next() // 继续执行函数 ,打印 A ,遇到 yield 2 停止执行 iterator.next() // 继续执行函数 ,打印 B ,遇到 yield 3 停止执行 iterator.next() // 继续执行函数 ,打印 Cnext 方法调用时 ,它是有返回值的 ,它的返回值就是 yield 后面的值或函数的返回值 。比如下面这个例子:
// 同步代码 function* gen() { yield 1 console.log(A) yield 2 console.log(B) yield 3 console.log(C) return 4 } // 定义迭代器对象 const iterator = gen() // 异步代码 console.log(iterator.next()) // 打印为空 next返回 {value:1,done:false} console.log(iterator.next()) // A next返回 {value:2,done:false} console.log(iterator.next()) // B next返回 {value:3,done:false} console.log(iterator.next()) // C next返回 {value:4,done:true},如果函数有return值 ,最后一个next方法 ,它的value值为return的值 value:4;如果没有 。值为 undefined拓展:其实之所以我们说 Generator 能够把异步变同步 ,是因为 Generator 函数中我们只需要写同步代码就可以 ,真正执行异步操作的是迭代器对象 。在复杂的业务逻辑中,大量使用迭代器对象来执行异步操作 ,会使得代码变得很不优雅 ,于是 ES7 中就推出了 async await 方案来实现异步变同步 。在 async await 方案中可以只书写同步代码,真正的异步操作被封装在底层 ,这样的写法 ,使得代码变优雅了很多 。
2. Generator中yield在赋值号左边的情况
yield 在等号右边时 ,它的返回值并不会返回给等号左边的变量 ,依然会返回给 next 方法 。
function* gen(num) { let r1 = yield 1 console.log(r1, r1); let r2 = yield 2 console.log(r2, r2); let r3 = yield 3 console.log(r3, r3); } const iterator = gen() console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next())这是因为 generator 函数在遇到 yield 时就已经暂停执行了 ,并不会执行到赋值操作 ,直到在执行完 next 方法之后 ,才会继续向下执行赋值操作 。如果我们想要 r1/r2/r3 有值 ,我们可以用 next 方法进行传值。就像下面这样:
function* gen(num) { let r1 = yield 1 console.log(r1, r1); let r2 = yield 2 console.log(r2, r2); let r3 = yield 3 console.log(r3, r3); } const iterator = gen() iterator.next() // 第一个 next 方法不用给值,即使给值也不会生效 iterator.next(A) iterator.next("B") iterator.next(C)3. Generator函数嵌套使用
function* gen1() { yield 1 yield 2 } function* gen2() { yield 3 // generator函数的嵌套 // 这种写法对应 方案1 // yield gen1() yield* gen1() yield 4 } const iterator = gen2() console.log(iterator.next()); // {value:3,done:false} // 如果我们想执行到 gen1 中的 yield 值 // console.log(iterator.next()); // {value:generator实例,done:false} // let itor = iterator.next().value // console.log(itor.next()); // {value:1,done:false} // console.log(itor.next()); // {value:2,done:false} // 方案2 console.log(iterator.next()); // {value:1,done:false} 你需要在yield后面加一个* ,让它知道后面是一个generator对象 console.log(iterator.next()); // {value:2,done:false} console.log(iterator.next()); // {value:4,done:false} console.log(iterator.next()); // {value:undefined,done:true}4. 使用generator函数完成网络请求
// 使用generator来完成异步网络请求 ,它还是要利用到promise // 模拟网络请求 function request(num = 1) { return new Promise((resolve, reject) => { return setTimeout(() => { resolve(++num) }, 1000); }) } // generator函数中的代码 ,发起的网络请求它就类似于同步写法 function* gen(num) { // yield右侧是一个promise对象 let r1 = yield request(10) console.log(r1, r1); let r2 = yield request(r1) console.log(r2, r2); let r3 = yield request(r2) console.log(r3, r3); } const iterator = gen(10) iterator.next().value.then(ret1 => { iterator.next(ret1).value.then(ret2 => { iterator.next(ret2).value.then(ret3 => { iterator.next(ret3) }) }) })上面的写法不够优雅 ,当有多个网络请求时,异步操作部分的代码会变得非常复杂 ,所以我们可以通过 co 库中的迭代函数来改写一下:
// 使用generator来完成异步网络请求 ,它还是要利用到promise // 模拟网络请求 function request(num) { return new Promise((resolve, reject) => { return setTimeout(() => { resolve(++num) }, 1000); }) } // generator函数中的代码,发起的网络请求它就类似于同步写法 function* gen(num) { // yield右侧是一个promise对象 let r1 = yield request(10) console.log(r1, r1); let r2 = yield request(r1) console.log(r2, r2); let r3 = yield request(r2) console.log(r3, r3); let r4 = yield request(r3) console.log(r4, r4); let r5 = yield ok console.log(r5, r5); } // 通过co库实现 function co(generator, ...params) { const iterator = gen(...params) // 迭代函数 const next = n => { let { value, done } = iterator.next(n) // 判断一下value它是一个promise对象 ,如果不是promise对象则需要手动转为promise对象 ,或抛异常 if (value != undefined && typeof value.then != "function") { throw new Error(必须为promise对象) // value = Promise.resolve(value) } if (done) return; // value.then(ret => next(ret)) value.then(next) } next(0) } co(gen, 100)创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!