文章目录

  • 1. Generator的定义和执行
  • 2. Generator中yield在赋值号左边的情况
  • 3. Generator函数嵌套使用
  • 4. 使用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 1console.log('A')yield 2console.log('B')yield 3console.log('C')return 4
}// 定义迭代器对象
const iterator = gen()
iterator.next() // 执行 gen 函数,打印为空,遇到 yield 1 停止执行
iterator.next() // 继续执行函数,打印 A,遇到 yield 2 停止执行
iterator.next() // 继续执行函数,打印 B,遇到 yield 3 停止执行
iterator.next() // 继续执行函数,打印 C

next 方法调用时,它是有返回值的,它的返回值就是 yield 后面的值或函数的返回值。比如下面这个例子:

// 同步代码
function* gen() {yield 1console.log('A')yield 2console.log('B')yield 3console.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 1console.log('r1', r1);let r2 = yield 2console.log('r2', r2);let r3 = yield 3console.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 1console.log('r1', r1);let r2 = yield 2console.log('r2', r2);let r3 = yield 3console.log('r3', r3);}
const iterator = gen()
iterator.next() // 第一个 next 方法不用给值,即使给值也不会生效
iterator.next('A')
iterator.next("B")
iterator.next('C')

3. Generator函数嵌套使用

function* gen1() {yield 1yield 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)

js中Generator函数详解相关推荐

  1. JS中箭头函数详解=

    箭头函数表达式的语法比函数表达式短,并且不绑定自己的 this,arguments,super或 new.target.此外,箭头函数总是匿名的.这些函数表达式最适合非方法函数,它们不能用作构造函数. ...

  2. 【ES6】Generator函数详解

    [ES6]Generator函数详解 一.Generator函数简介 基本概念 函数写法 yield关键字介绍 二.next方法的参数 三.for...of循环 四.关于普通throw()与Gener ...

  3. computed用发_Vue.js中computed使用详解

    这次给大家带来Vue.js中computed使用详解,Vue.js中computed使用的注意事项有哪些,下面就是实战案例,一起来看一下. JS属性: JavaScript有一个特性是Object.d ...

  4. Matlab中 pdist 函数详解

    学习笔记,仅供参考,有错必纠 转载自:Matlab中 pdist 函数详解(各种距离的生成); pdist Pairwise distance between pairs of objects. Sy ...

  5. c++ memset 语言_C/C++ 中memset() 函数详解及其作用介绍

    memset 函数是内存赋值函数,用来给某一块内存空间进行赋值的: 包含在头文件中,可以用它对一片内存空间逐字节进行初始化: 原型为 : void *memset(void *s, int v, si ...

  6. matlab中xcorr函数详解

    Matlab中xcorr函数详解 引言 在matlab中查找相关函数时,找到了xcov函数和xcorr函数,仔细看了帮助文档,发现虽然计算公式不一样,但是两个函数用法相同,计算出的值也相同,于是在翻了 ...

  7. Linux中fork()函数详解

    Linux中fork()函数详解 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事, ...

  8. linux 中 sigaction 函数详解

    linux 中 sigaction 函数详解 一.函数原型 sigaction 函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作) int sigaction(int signum, ...

  9. JS中Generator函数的详解

    概念     Generator 函数是 ES6 提供的一种异步编程解决方案.它既是一个生成器,也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器 Iterator 对象,我们可以通过这个迭 ...

最新文章

  1. es获取最大时间的记录_ES查询一段时间内某一循环时间段的数据
  2. 开发日记-20190712 关键词 读书笔记 《Perl语言入门》Day 8
  3. Excel中的的经纬度坐标在地图上显示
  4. Go操作mysql实现增删改查及连接池
  5. [小程序]小程序框架的简单页面布局
  6. [Idea Fragments]2013.08.08
  7. sql 除以_刷完这些SQL练习题,简单查询就熟能生巧了
  8. megento 获取url参数
  9. 给开源项目贡献代码的经历
  10. Selenium分布式运行:SeleniumGrid
  11. Windows10安装JDK配置环境变量
  12. PHP - 经典面试题大全,看这一篇就够了
  13. SPSS(一)进行单因素方差分析及多重比较检验(图文教程)
  14. le jour, la date et lès saisons
  15. 机器学习 卷积神经网络 Convolutional Neural Network(CNN)
  16. 电子商务站点资源收集~~~
  17. 基于翻译的模型-TransE,TransH,TransR,TransD
  18. c语言printf打印浮点型,printf以%d输出浮点数
  19. C#快速随机按行读取大型文本文件 - 磊的博客 - sanshi_leilei - 和讯博客
  20. 财路网每日原创推送:区块链中的“块”是什么?

热门文章

  1. 视觉-惯性SLAM入门与实践教程(基于VINS-Fusion)
  2. ICPC Latin American Regional Contests 2019 K.Know your Aliens菜鸡版
  3. 金蝶EAS BOS F7按钮自定义弹窗
  4. 关于微信小程序开发里,数据动态绑定,显示改变,值不改变的记录
  5. python中list的意思_python中列表和集合有什么区别
  6. 从程序员到项目经理(十二):如何管理自己的时间(上)
  7. 基于vuex的物业管理系统APP及网页后台设计
  8. qt4.8 利用串口实现智能大棚系统
  9. 程序员的必备网站 - 合集(找资源?看我一个就够了)
  10. 轻度Linux患者福利,Win10安装Linux子系统教程,附 Docker 安装教程