迭代器&生成器

Iterable

  • 实现了Iterable接口的数据结构就可以通过Iterator消费
  • Iterator暴露可迭代对象的api,无需了解他的数据结构

怎么实现Iterable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Counter {
constructor(limit) {
this.limit = limit
}
//1. 工厂函数
[Symbol.iterator]() {
let count = 1;
let limit = this.limit
return {
// 2.1 next方法
next() {
if (count <= limit)
// 返回IterableResult
return { done: false, value: count++ }
else return { done: true, value: undefined }
}
//2.2 可选 提前终止迭代器
return() {
console.log('提前终止...')
//必须返回IterableResult
return { done: true }
}
}
}
}
let counter = new Counter(3)
for (let i of counter) {
console.log(i)
}
1
2
3
4
5
6
7
8
9
10
11
// 提前中断
for (let i of counter) {
console.log(i)
if (i == 2)
break
}
/*
1
2
提前终止...
*/

迭代器如果没有关闭可以接着之前的位置继续。中断迭代器会调用return,但是调用了return不一定会关闭迭代器。例如Array类型

next()

next() 是迭代器的api

  • 返回Iteratorresult对象, 包括 done和value
    • done: true | false 迭代完了吗
    • value: 当前取得
1
console.log(ita.next()); //{ value: 'a', done: false } 

内部实现了Iterable接口的类型

  1. String
  2. Array
  3. Set
  4. Map
  5. arguments 对象
  6. NodeList 等DOM集合类型
1
2
3
4
5
6
7
let arr = ['a','b']

let it = arr[Symbol.iterator]
console.log(it); //[Function: values] 迭代器的工厂函数

let ita = arr[Symbol.iterator]()
console.log(ita); //Object [Array Iterator] {} 迭代器

自动兼容可迭代对象的

它们会自动调用工厂函数创建迭代器

  1. for-of
  2. 数组解构 let [a, b] = [1,2]
  3. 扩展操作符 [1,2,3, ...arr2]
  4. 创建Set、Map
  5. Promise.all()
  6. Array.from()
  7. yield*

伪数组没有迭代器,需要Array.from()转成数组

1
2
3
4
5
6
7
8
9
10
11
let fakerArr = {
0: 'a',
1: 'b',
2: 'c',
}
for( let i in fakerArr){
console.log(fakerArr[i]) //正常
}
for( let item of fakerArr){ //TypeError: fakerArr is not iterable
console.log(item)
}

注意

  1. 迭代器会占用一个引用计数,组织垃圾回收可迭代对象

生成器

拥有在一个函数块内暂停恢复代码执行的能力

生成器函数

  • 声明生成器函数
1
function * generatorFn(){} //调用后返回生成器对象

箭头函数不能声明为生成器函数

生成器对象

  • 一开始处于suspended状态
  • 实现了Iterator接口
    • next()
      • 让生成器开始或恢复执行
    • return() 必有
      • 即value的值
    • throw() 特有
      • 注入一个错误,如果不用try-catch处理错误就会关闭生成器,处理了则跳过这次yield继续下去
1
2
3
4
5
6
7
8
9
10
11
12
function* generatorFn() {
console.log('log...')
return 'foo'
}
const g = generatorFn()
console.log(g);
console.log(g.next()) //到这步才会打印log,因为之前处于暂停状态
/*
generatorFn {<suspended>}
log...
{ value: 'foo', done: true }
*/
  • 默认的迭代器是自引用的
1
2
const g = generatorFn();
console.log(g === g[Symbol.iterator]()) //true

yield

  1. yield 可以让生成器停止和开始执行
    1. 使用next()继续执行
  2. yield 退出的生成器函数处于done: false状态
    1. return() 出去的done: true
  3. yield 只能在生成器函数内使用
1
2
3
4
5
6
7
8
9
10
11
//用next()继续
function * generatorFn() {
//yield 来输出
yield 'a'
yield 'b'
return '0'
}
const g = generatorFn(); //生成器对象
console.log(g.next()) //{value: 'a', done: false}
console.log(g.next()) //{value: 'b', done: false}
console.log(g.next()) //{value: '0', done: true}
1
2
3
4
5
6
7
8
//生成器对象作为可迭代对象
for(const x of generatorFn()){
console.log(x)
}
/*
a
b
*/
  1. yield 实现输入输出

yield接收next里的参数,除了第一个next, 其作用是 启动生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
function * generatorFn(initial) {
console.log(initial);
//yield输入
console.log(yield);
console.log(yield);
console.log(yield);
}
const g = generatorFn('initial')
g.next('a') //initial ,不会输出a
g.next('b') //b
g.next('c') //c
g.next('d') //d

yield同时输入输出

1
2
3
4
5
6
function * generatorFn() {
return yield 'foo'
}
const g = generatorFn()
console.log(g.next()); //{value: 'foo', done: false}
console.log(g.next('x00')); //{value: 'x00', done: true}

yield*

增强yield行为,让他可以迭代一个可迭代对象

1
2
3
function * generatorFn(){
yield* [1,2,3]
}

适合作默认迭代器

1
2
3
4
5
6
7
8
9
10
11
class Foo {
constructor(){
this.value = [1,2,3]
}
* [Symbol.iterator](){
yield* this.value
}
}
for(let f of new Foo()){
console.log(f)
}

配合 Promise 实现异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function* readFileWithGen() {
try {
const content1 = yield readFileWithPromise('/etc/passwd', 'utf8')
console.log(content1)
const content2 = yield readFileWithPromise('/etc/profile', 'utf8')
console.log(content2)
return 'done'
} catch (err) {
console.error(err)
return 'fail'
}
}

const run = generator => {
return new Promise((resolve, reject) => {
const g = generator()
const next = res => {
const result = g.next(res)
if (result.done) {
return resolve(result.value)
}
result.value
.then(
next,
err => reject(gen.throw(err).value)
)
}
next()
})
}

run(readFileWithGen)
.then(res => console.log(res))
.catch(err => console.log(err))

🔗async/await原理和co函数实现

实现async/await

  • async 会把函数变成Promise对象
  • async函数就是generator 函数,把 function* 替换成 async function ,把 yield 替换成 await
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const getFetch = (nums) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(nums + 1);
}, 1000);
});

// 每一个异步操作依赖于上一步异步操作的返回值。
function* gen() {
let res1 = yield getFetch(1);
let res2 = yield getFetch(res1);
let res3 = yield getFetch(res2);
return res3;
}
// 让next函数递归执行
function myAsync(gen){
return ()=> new Promise((resolve,reject)=> {
let g = gen();
const next = (context)=>{
const {done, value} = g.next(context);
if(done)
resolve(value)
else
return Promise.resolve(value).then(val=>next(val))
}
next();
})
}