文章目录
1.迭代器
1.1 认识迭代器
迭代器(iterator),是使用户在容器对象(container,例如链表或数组)上可以遍历访问的对象,使用该接口无需关心容器对象的内部实现细节。
- 其行为像数据库中的光标,迭代器最早出现在1974年设计的CLU编程语言中;
- 在各种编程语言的实现中,迭代器的实现方式各不相同,但是基本都有迭代器,比如Java、Python等;
从迭代器的定义我们可以看出来,迭代器是帮助我们对某个数据结构进行遍历的对象。
在JavaScript中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol):
- 迭代器协议定义了产生一系列值(无论是有限还是无限个)的标准方式;
- 在JavaScript中这个标准就是一个特定的next方法;
next方法要求:next方法是一个无参数或者有一个参数的函数,返回结果应当拥有以下两个属性的对象:
1.done(boolean) : done返回一个布尔值: 可能是false, 可能是true
- 如果迭代器可以产生序列中的下一个值,则为 false。(这等价于没有指定 done 这个属性。)
- 如果迭代器已将序列迭代完毕,则为 true。这种情况下,value 是可选的,如果它依然存在,即这个值为迭代结束之后默认返回值。
2. value:value的返回值: 可能是一个具体的值, 可能是undefined
- 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。
1.2 理解迭代器
上面的概念可能会有些抽象 , 下面我们来看看代码帮助我们理解next方法需要满足的要求 :
- 创建一个满足要求的迭代器
const arr = [10, 20, 30]
// 给数组arr创建一个迭代器
// 1.迭代器是一个对象 首先我们创建一个对象
const arrIterator = {
// 2.迭代器中有一个next方法, 这个函数要求无参数或者仅有一个参数, 在对象中添加next方法
next: function() {
// 3.next方法需要返回一个对象, 且对象中需要包含done和value两个属性
return {done: false, value: 10}
}
}
- 但是这个迭代器对象还并不能帮助我们对数组进行迭代, 我们需要实现如下功能:
// 第一次调用迭代器, 由于没有完成所有的迭代, done应该为false, value应该为10
console.log(arrIterator.next())
// 第二次调用迭代器, 由于没有完成所有的迭代, done应该为false, value应该为20
console.log(arrIterator.next())
// 第三次调用迭代器, 由于没有完成所有的迭代, done应该为false, value应该为30
console.log(arrIterator.next())
// 第四次调用迭代器, 由于已经完成所有的迭代, done应该为true, value应该为undefined
console.log(arrIterator.next())
- 添加一个if添加语句, 我们就可以实现如上功能, 但是这个迭代器是arr这个数组独有的迭代器, 不具备通用性
const arr = [10, 20, 30]
// 给数组arr创建一个迭代器
let index = 0
// 1.迭代器是一个对象 首先我们创建一个对象
const arrIterator = {
// 2.迭代器中有一个next方法, 这个函数要求无参数或者仅有一个参数, 在对象中添加next方法
next: function() {
// 3.next方法需要返回一个对象, 且对象中需要包含done和value两个属性
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done:true, value: undefined }
}
}
}
console.log(arrIterator.next()) // {done: false, value: 10}
console.log(arrIterator.next()) // {done: false, value: 20}
console.log(arrIterator.next()) // {done: false, value: 30}
console.log(arrIterator.next()) // {done: true, value: undefined}
- 我们封装一个函数, 让上面我们实现的迭代器具有通用性
// 例如有两个数组
const nums = [10, 20, 30, 40]
const names = ["aaa", "bbb", "ccc", "ddd"]
// 封装一个用于创建迭代器的函数
function createArrayIterator(arr) {
let index = 0
return {
next: function() {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
// value不写, 默认就是undefined
return { done: true }
}
}
}
}
// 调用函数创建nums构造器
const numsIterator = createArrayIterator(nums)
console.log(numsIterator.next()) // {done: false, value: 10}
console.log(numsIterator.next()) // {done: false, value: 20}
console.log(numsIterator.next()) // {done: false, value: 30}
console.log(numsIterator.next()) // {done: false, value: 40}
console.log(numsIterator.next()) // {done: true}
// 调用函数创建names构造器
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next()) // {done: false, value: 'aaa'}
console.log(namesIterator.next()) // {done: false, value: 'bbb'}
console.log(namesIterator.next()) // {done: false, value: 'ccc'}
console.log(namesIterator.next()) // {done: false, value: 'ddd'}
console.log(namesIterator.next()) // {done: true}
2.可迭代对象
2.1 认识可迭代对象
但是上面的代码整体来说看起来是有点奇怪的:
- 我们获取一个数组的时候,需要自己创建一个index变量,再创建一个所谓的迭代器对象;
事实上我们可以对上面的代码进行进一步的封装,让其变成一个可迭代对象;
- 我们再来看一下类似于刚刚的代码
const info = {
friends: ["aaa", "bbb", "ccc"]
}
let index = 0
// 创建一个info的迭代器
const infoIterator = {
next: function() {
if (index < info.friends.length) {
return { done: false, value: info.friends[index++] }
} else {
return { done: true }
}
}
}
console.log(infoIterator.next()) // {done: false, value: 'aaa'}
console.log(infoIterator.next()) // {done: false, value: 'bbb'}
console.log(infoIterator.next()) // {done: false, value: 'ccc'}
console.log(infoIterator.next()) // {done: true}
- 我们发现, 想要迭代的对象和我们的迭代器是分开的, 那么有没有办法就将要迭代的对象和迭代器合并起来呢?(往下面看)
什么又是可迭代对象呢?
- 可迭代对象和迭代器是两个不同的概念, 其实我们将上面的代码中, 迭代的目标对象和迭代器合并起来就一个可迭代对象;
- 当一个对象实现了iterable protocol协议时,它就是一个可迭代对象;
- 可迭代对象的要求一 : 是必须实现 @@iterator (这是规范的名字) 方法,在代码中我们使用 [Symbol.iterator] (这是实际用的名字)访问该属性;
- 可迭代对象的要求二 : 这个[Symbol.iterator] 方法需要返回一个迭代器
- 那么我们根据这两个要求, 试着将上面代码中的info对象变成一个可迭代对象
const info = {
friends: ["aaa", "bbb", "ccc"],
// 1.必须实现一个特定的方法[Symbol.iterator] (名字是固定的)
[Symbol.iterator] () {
// 将我们创建的info对象的迭代器放过来
let index = 0
const infoIterator = {
next: function() {
if (index < info.friends.length) {
return { done: false, value: info.friends[index++] }
} else {
return { done: true }
}
}
}
// 2.这个方法需要返回一个迭代器(这个迭代器用于迭代当前对象)
return infoIterator
}
}
- 为了进一步优化, 我们想在next方法中使用this, 但是next方法中的this不一定指向info, 我们需要让this指向info
const info = {
friends: ["aaa", "bbb", "ccc"],
[Symbol.iterator] () {
let index = 0
const infoIterator = {
// 将next方法改为箭头函数, 这样next方法中就不在绑定this, this回去上层作用域中寻找, 上层作用域中的this会指向info
next: () => {
if (index < this.friends.length) {
return { done: false, value: this.friends[index++] }
} else {
return { done: true }
}
}
}
return infoIterator
}
}
当然我们要问一个问题,我们转成这样的一个东西有什么好处呢?
-
当一个对象变成一个可迭代对象的时候,就可以进行某些迭代操作;
-
比如 for…of 操作时(当然不仅限于for…of),其实就会调用它的 [Symbol.iterator] 方法(@@iterator);
// 例如上面的info对象本来是没办法for...of操作的, 变成可迭代对象后就可以进行for...of操作 for (item of info) { console.log(item) // aaa bbb ccc }
2.2 可迭代对象特点
上面我们将info变成可迭代对象可以发现可迭代对象会具备一下特点:
-
可迭代对象我们一定**可以访问它的[Symbol.iterator]**方法
info[Symbol.iterator]
-
调用 [Symbol.iterator]方法一定会返回一个迭代器
const iterator = info[Symbol.iterator]()
-
可以调用返回的迭代器的next方法, 对 对象进行迭代
const iterator = info[Symbol.iterator]() console.log(iterator.next()) // {done: false, value: 'aaa'} console.log(iterator.next()) // {done: false, value: 'bbb'} console.log(iterator.next()) // {done: false, value: 'ccc'} console.log(iterator.next()) // {done: true}
2.3 原生迭代器对象
事实上我们平时创建的很多原生对象已经实现了可迭代协议,会生成一个迭代器对象的:
- 比如 : String、Array、Map、Set、arguments对象、NodeList集合;
我们都知道数组是一个可迭代对象, 那么数组是不是有[Symbol.iterator]方法并且满足我们刚刚总结的特点呢? 我们来验证一下
-
打印数组的[Symbol.iterator]方法, 我们发现确实有这样一个函数
const nums = [10, 20, 30] console.log(nums[Symbol.iterator]) // ƒ values() { [native code] }
-
调用数组的[Symbol.iterator]方法, 我们发现会返回一个数组的迭代器
const nums = [10, 20, 30] console.log(nums[Symbol.iterator] ()) // Array Iterator {}
-
并且我们可以拿到这个数组的迭代器, 通过迭代器的next方法访问数组元素
const nums = [10, 20, 30] const iterator = nums[Symbol.iterator]() console.log(iterator.next()) // {value: 10, done: false} console.log(iterator.next()) // {value: 20, done: false} console.log(iterator.next()) // {value: 30, done: false} console.log(iterator.next()) // {value: undefined, done: true}
由此发现, 数组中是有一个实现好了的迭代器, 因此数组是一个可迭代对象, 数组可迭代的原理我们就很清楚了, 其他可迭代对象同理
2.4 可迭代对象场景
**上面我们已经讲过, 可迭代对象可以使用for…of, 那么除此之外还可以应用在以下场景 **:
- JavaScript中语法:for …of、展开语法(spread syntax)、yield*(后面讲)、解构赋值(Destructuring_assignment);
- 创建一些对象时:new Map([Iterable])、new WeakMap([iterable])、new Set([iterable])、new WeakSet([iterable]);
- 一些方法的调用:Promise.all(iterable)、Promise.race(iterable)、Array.from(iterable);
简单举几个例子:
// 例如两个对象, 一个转为可迭代对象的info, 一个是默认对象obj
const obj = {
name: "kaisa",
age: 18
}
const info = {
friends: ["aaa", "bbb", "ccc"],
[Symbol.iterator] () {
let index = 0
const infoIterator = {
next: () => {
if (index < this.friends.length) {
return { done: false, value: this.friends[index++] }
} else {
return { done: true }
}
}
}
return infoIterator
}
}
// 1.可迭代对象可以使用展开语法
console.log(...info) // aaa bbb ccc
// 普通对象不可使用展开语法
console.log(...obj) // Found non-callable @@iterator
// 2.set方法也是要求传入可迭代对象
const set1 = new Set(info)
console.log(set1) // Set(3) {'aaa', 'bbb', 'ccc'}
// 普通对象传入就会报错
const set2 = new Set(obj)
console.log(set2)
// 3.转为数组的方法Array.from也是要求传入可迭代对象
const arr1 = Array.from(info)
console.log(arr1) // ['aaa', 'bbb', 'ccc']
// 不可迭代对象无法正确转为数组
const arr2 = Array.from(obj)
console.log(arr2) // []
2.5 自定义类的迭代
在前面我们看到Array、Set、String、Map等类创建出来的对象都是可迭代对象:
- 在面向对象开发中,我们可以通过class定义一个自己的类,这个类可以创建很多的对象:
- 如果我们也希望自己的类创建出来的对象默认是可迭代的,那么在设计类的时候在类的实例方法上添加迭代器;
自定义类的迭代的实现: 写一个案例尝试创建一系列可迭代对象
class Person {
constructor(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
// 添加实例方法, 迭代器
[Symbol.iterator] () {
let index = 0
return {
next: () => {
if (index < this.friends.length) {
return { done: false, value: this.friends[index++] }
} else {
return { done: true }
}
}
}
}
}
// 这样Person类创建出来的对象都是可迭代对象
const p1 = new Person("kaisa", 18, ["aaa", "bbb", "ccc"])
const p2 = new Person("coder", 19, ["ddd", "eee", "fff"])
// 可以进行for...of操作
for (item of p1) {
console.log(item) // aaa bbb ccc
}
for (item of p2) {
console.log(item) // ddd eee fff
}
2.6 迭代器的中断
迭代器在某些情况下会在没有完全迭代的情况下中断:
- 如遍历的过程中通过break、return、throw中断了循环操作;
- 比如在解构的时候,没有解构所有的值;
那么这个时候我们想要监听中断的话,可以添加return方法:
- 例如我们用上面的代码举例
class Person {
constructor(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
// 添加实例方法, 迭代器
[Symbol.iterator] () {
let index = 0
return {
next: () => {
if (index < this.friends.length) {
return { done: false, value: this.friends[index++] }
} else {
return { done: true }
}
},
// 添加一个return方法, 用于监听迭代中断, 当迭代器中断就会执行return方法
return: () => {
console.log("监听到迭代器中断")
// 迭代器需要返回对象
return { done:true }
}
}
}
}
const p1 = new Person("kaisa", 18, ["aaa", "bbb", "ccc"])
for (item of p1) {
console.log(item) // aaa bbb
// 如果在某种情况写退出了循环, 我们需要告知迭代器
if (item === "bbb") {
break
}
}
3.生成器介绍
3.1 什么是生成器
生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
- 平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。
生成器函数也是一个函数,但是和普通的函数有一些区别:
- 首先,生成器函数需要在function的后面加一个符号:*
- 其次,生成器函数可以通过yield关键字来控制函数的执行流程
- 最后,生成器函数的返回值是一个Generator(生成器)
生成器事实上是一种特殊的迭代器
3.2 生成器函数执行
我们在代码中演示一下:
- 生成器需要在function后面添加一个符号 *
// 定义了一个生成器函数
function* foo() {
console.log("00001")
console.log("00002")
console.log("00003")
console.log("00004")
console.log("00005")
console.log("00006")
}
foo()
- 生成器代码的执行可以被yield控制
// 定义了一个生成器函数
function* foo() {
console.log("00001")
console.log("00002")
yield
console.log("00003")
console.log("00004")
yield
console.log("00005")
console.log("00006")
}
foo()
-
生成器函数默认在执行时, 返回的也是一个生成器对象, 我们发现上面代码中, 调用函数时函数内部的代码并没有执行
如果想要执行函数内部的代码, 需要调用返回的生成器对象的next方法
当函数内部代码, 遇到yield时会中断执行
// 1.定义了一个生成器函数
function* foo() {
console.log("00001")
console.log("00002")
yield console.log("aaaaa")
console.log("00003")
console.log("00004")
yield
console.log("00005")
console.log("00006")
}
// 2.调用生成器函数, 会返回一个生成器对象
const generator = foo()
// 当遇到yield时 会执行到与yield右边的代码后中断 yield左边如果有代码不执行 例如上面第一个yield后面还有代码也会执行
generator.next() // 00001 00002 aaaaa
generator.next() // 00003 00004
generator.next() // 00005 00006
- 我们之前学习迭代器时,知道迭代器的next是会有返回值的, 而生成器也是一个特殊的迭代器, 那么我们看一下生成器调用next方法会返回什么
function* foo() {
console.log("00001")
console.log("00002")
yield
console.log("00003")
console.log("00004")
yield
console.log("00005")
console.log("00006")
}
// 生成器返回一个生成器
const generator = foo()
console.log(generator.next()) // {value: undefined, done: false}
console.log(generator.next()) // {value: undefined, done: false}
console.log(generator.next()) // {value: undefined, done: true}
- 但是我们很多时候不希望next返回的是一个undefined,这个时候我们可以通过yield来返回结果;
function* foo() {
console.log("00001")
console.log("00002")
yield "aaaaa"
console.log("00003")
console.log("00004")
yield "bbbbb"
console.log("00005")
console.log("00006")
}
// 生成器返回一个生成器
const generator = foo()
console.log(generator.next()) // {value: "aaaaa", done: false}
console.log(generator.next()) // {value: "bbbbb", done: false}
console.log(generator.next()) // {value: undefined, done: true}
3.3 生成器传递参数
函数既然可以暂停来分段执行,那么函数应该是可以传递参数的,我们是否可以给每个分段来传递参数呢?
- 答案是可以的, 我们在调用next函数的时候,可以给它传递参数,那么这个参数会作为上一个yield语句的返回值;
function* foo(name1) {
console.log("00001", name1)
console.log("00002", name1)
const name2 = yield "aaaaa"
console.log("00003", name2)
console.log("00004", name2)
const name3 = yield "bbbbb"
console.log("00005", name3)
console.log("00006", name3)
}
// 生成器返回一个生成器
const generator = foo("name1")
// 第一次传递参数是通过函数传递
console.log(generator.next("name1"))
// 第二次及后面开始传递参数, 是通过yield
console.log(generator.next("name2"))
console.log(generator.next("name3"))
3.4 生成器提前结束
还有一个可以给生成器函数传递参数的方法是通过return函数:
- return传值后这个生成器函数就会结束,之后调用next不会继续生成值了;
function* foo(name1) {
console.log("00001", name1)
console.log("00002", name1)
const name2 = yield "aaaaa"
console.log("00003", name2)
console.log("00004", name2)
const name3 = yield "bbbbb"
console.log("00005", name3)
console.log("00006", name3)
}
// 生成器返回一个生成器
const generator = foo("name1")
console.log(generator.next("name1")) // {value: 'aaaaa', done: false}
// 通过return提前结束生成器
console.log(generator.return("name2")) // {value: 'name2', done: true}
// return之后再通过next调用不会继续生成值
console.log(generator.next("name3")) // {value: undefined, done: true}
console.log(generator.next()) // {value: undefined, done: true}
console.log(generator.next()) // {value: undefined, done: true}
3.5 生成器抛出异常
除了给生成器函数内部传递参数之外,也可以给生成器函数内部抛出异常:
- 抛出异常后我们可以在生成器函数中捕获异常;
- 但是在catch语句中不能继续yield新的值了,但是可以在catch语句外使用yield继续中断函数的执行;
- 目前未学习捕获异常语句, 了解即可
function* foo(name1) {
console.log("00001", name1)
console.log("00002", name1)
const name2 = yield "aaaaa"
console.log("00003", name2)
console.log("00004", name2)
const name3 = yield "bbbbb"
console.log("00005", name3)
console.log("00006", name3)
}
// 生成器返回一个生成器
const generator = foo("name1")
console.log(generator.next("name1")) // {value: 'aaaaa', done: false}
console.log(generator.throw("name2 throw")) // Uncaught name2 throw
console.log(generator.return("name2"))
console.log(generator.next("name3"))
4.生成器应用
4.1 生成器替代迭代器
我们发现生成器是一种特殊的迭代器,那么在某些情况下我们可以使用生成器来替代迭代器:
- 我们前面学习迭代器的时候, 有写过下面这样一个案例用于实现一个迭代器
function createArrayIterator(arr) {
let index = 0
return {
next: function() {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true }
}
}
}
}
- 我们可以利用生成器替代迭代器进行优化
const names = ["aaa", "bbb", "ccc"]
function* createArrayIterator(arr) {
for (let i = 0; i < arr.length; i++) {
yield arr[i]
}
}
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next()) // {value: 'aaa', done: false}
console.log(namesIterator.next()) // {value: 'bbb', done: false}
console.log(namesIterator.next()) // {value: 'ccc', done: false}
console.log(namesIterator.next()) // {value: undefined, done: true}
- 事实上我们还可以使用yield*来生产一个可迭代对象:这个时候相当于是一种yield的语法糖,只不过会依次迭代这个可迭代对象,每次迭代其中的一个值;
const names = ["aaa", "bbb", "ccc"]
function* createArrayIterator(arr) {
yield* arr
}
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next()) // {value: 'aaa', done: false}
console.log(namesIterator.next()) // {value: 'bbb', done: false}
console.log(namesIterator.next()) // {value: 'ccc', done: false}
console.log(namesIterator.next()) // {value: undefined, done: true}
生成器案例: 定义一个生成器, 可以创建某个范围的值
- 例如: 生成 [3, 9) 之间的值
function* createRangeGenerator(start, end) {
for (let i = start; i < end; i++) {
yield i
}
}
const numsGenerator = createRangeGenerator(3, 9)
console.log(numsGenerator.next()) // {value: 3, done: false}
console.log(numsGenerator.next()) // {value: 4, done: false}
console.log(numsGenerator.next()) // {value: 5, done: false}
console.log(numsGenerator.next()) // {value: 6, done: false}
console.log(numsGenerator.next()) // {value: 7, done: false}
console.log(numsGenerator.next()) // {value: 8, done: false}
console.log(numsGenerator.next()) // {value: undefined, done: true}
4.2 生成器实现自定义类迭代
在迭代器的时候我们创建过一个自定义类, 用于创建一系列可迭代对象 :
class Person {
constructor(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
// 添加实例方法, 迭代器
[Symbol.iterator] () {
let index = 0
return {
next: () => {
if (index < this.friends.length) {
return { done: false, value: this.friends[index++] }
} else {
return { done: true }
}
}
}
}
}
// 这样Person类创建出来的对象都是可迭代对象
const p1 = new Person("kaisa", 18, ["aaa", "bbb", "ccc"])
// 可以进行for...of操作
for (item of p1) {
console.log(item) // aaa bbb ccc
}
这个自定义类我们也可以换成生成器, 对上面代码优化:
class Person {
constructor(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
}
// 添加实例方法, 迭代器前面加上*变为生成器
*[Symbol.iterator] () {
// yield*后面跟要迭代的对象
yield* this.friends
}
}
// 进行for...of操作
const p1 = new Person("kaisa", 18, ["aaa", "bbb", "ccc"])
for (item of p1) {
console.log(item) // aaa bbb ccc
}
5.生成器异步处理
5.1 普通处理方案
学完了我们前面的Promise、生成器等,我们目前通过一个案例来看一下异步代码的处理方案。
案例需求 :
- 我们需要向服务器发送网络请求获取数据,一共需要发送三次请求;
- 第二次的请求url依赖于第一次的结果;
- 第三次的请求url依赖于第二次的结果, 依次类推;
方式一 : 层层嵌套(回调地狱)
// 封装函数模拟网络请求
function requestDate(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
function getData() {
// 发送第一次网络请求
requestDate("aaa").then(res1 => {
console.log("第一次网络请求的结果:", res1) // aaa
// 发送第二次网络请求
requestDate(res1 + "bbb").then(res2 => {
console.log("第二次网络请求的结果:", res2) // aaabbb
// 发送第三次网络请求
requestDate(res2 + "ccc").then(res3 => {
console.log("第三次网络请求的结果:", res3) // aaabbbccc
})
})
})
}
getData()
方式二: 使用Promise进行优化, 解决回调地狱(链式编程)
// 封装函数模拟网络请求
function requestDate(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
function getData() {
requestDate("aaa").then(res1 => {
console.log("第一次网络请求的结果:", res1) // aaa
return requestDate(res1 + "bbbb")
}).then(res2 => {
console.log("第二次网络请求的结果:", res2) // aaabbb
return requestDate(res2 + "ccc")
}).then(res3 => {
console.log("第三次网络请求的结果:", res3) // aaabbbccc
})
}
getData()
5.2 生成器方案
上面的代码即使方式二使用Promise对代码进行了重构, 但是代码看起来也是阅读性依然比较差的,有没有办法可以继续来对上面的代码进行优化呢?
- 方式三: 利用生成器再进一步的优化
// 封装函数模拟网络请求
function requestDate(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
function* getData() {
const res1 = yield requestDate("aaa")
console.log("res1", res1) // aaa
const res2 = yield requestDate(res1 + "bbb")
console.log("res2", res2) // aaabbb
const res3 = yield requestDate(res2 + "ccc")
console.log("res3:", res3) // aaabbbccc
}
const generator = getData()
// 因为生成器的next方法返回的对象中的value值是返回的Promise
// 我们可以通过Promise的then拿到第一次网络请求的结果res1
generator.next().value.then(res1 => {
// 将第一次网络请求的结果传入生成器函数时, 可以拿到第二次网络请求的结果
generator.next(res1).value.then(res2 => {
// 将第二次网络请求的结果传入生成器函数, 可以拿到第三次网络请求的结果
generator.next(res2).value.then(res3 => {
// 将第三次网络请求的结果传入生成器函数
generator.next(res3)
})
})
})
目前我们的方案三写法有两个问题:
- 第一,我们不能确定到底需要调用几层的Promise关系;
- 第二,如果还有其他需要这样执行的函数,我们应该如何操作呢?
所以,我们可以封装一个工具函数execGenerator自动执行生成器函数(了解):
// 封装模拟网络请求的函数
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
// 生成器的处理方案
function* getData() {
const res1 = yield requestData("why")
console.log("res1:", res1)
const res2 = yield requestData(res1 + "kobe")
console.log("res2:", res2)
const res3 = yield requestData(res2 + "james")
console.log("res3:", res3)
const res4 = yield requestData(res3 + "curry")
console.log("res4:", res4)
const res5 = yield requestData(res4 +"tatumu")
console.log("res5:", res5)
}
// 自动化执行生成器函数(了解)
function execGenFn(genFn) {
// 1.获取对应函数的generator
const generator = genFn()
// 2.定义一个递归函数
function exec(res) {
const result = generator.next(res)
if (result.done) return
result.value.then(res => {
exec(res)
})
}
// 3.执行递归函数
exec()
}
函数execGenerator自动执行生成器函数(了解):
// 封装模拟网络请求的函数
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000)
})
}
// 生成器的处理方案
function* getData() {
const res1 = yield requestData("why")
console.log("res1:", res1)
const res2 = yield requestData(res1 + "kobe")
console.log("res2:", res2)
const res3 = yield requestData(res2 + "james")
console.log("res3:", res3)
const res4 = yield requestData(res3 + "curry")
console.log("res4:", res4)
const res5 = yield requestData(res4 +"tatumu")
console.log("res5:", res5)
}
// 自动化执行生成器函数(了解)
function execGenFn(genFn) {
// 1.获取对应函数的generator
const generator = genFn()
// 2.定义一个递归函数
function exec(res) {
const result = generator.next(res)
if (result.done) return
result.value.then(res => {
exec(res)
})
}
// 3.执行递归函数
exec()
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/120127.html