Generator & async await

生成器 生成迭代器的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 返回值叫迭代器
function * read(){ // 这个叫生成器函数
yield 1; //产出
yield 2;
yield 3;
}
// iterator 迭代器
let it = read();
console.log(it);
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
// 可以把上面都执行下 每次执行到yield都会停一下,执行next()才会继续走

将类数组转化成数组

1
2
3
4
5
6
7
8
9
10
11
12
// 类数组的定义: 1 索引 2 长度
function add(){
console.log([...arguments]); // 这里相当于把类数组通过...展开 在放在一个[]数组里
}
add(1,2,3,4); // [ 1, 2, 3, 4 ]

function add(){
console.log([...{0:1, 1:2}]);
}
add(1,2,3,4);
// 运行如上这个line8 报错 TypeError: {(intermediate value)(intermediate value)} is not iterable
// 因为 ... 或者for of 运行它们 必须要提供一个生成器方法 知识点:for of

如果想要可用 如下 提供一个方法

1
2
3
4
5
6
7
8
9
10
11
function add(){
console.log([...{0:1, 1:2, [Symbol.iterator](){
// 迭代器 是有next方法 而且执行后 需要返回value,done
return {
next(){
return {value: 1, done: true}
}
}
}}]);
}
add(); // []

继续完善

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function add(){
console.log([...{
0:1,
1:2,
length: 2,
[Symbol.iterator](){ // 迭代器
let len = this.length;
let index = 0;
return {
next:()=>{
// 这里面谁调这个next 肯定是line6这个函数去掉它的对象next
// 这个对象叫it 所以是it.next 所以这里用箭头函数
// index++ 第一次的值是0 即line3 所以这里是++
// done 这里用index和当前的len相比 如果相等了就终止了
return {value: this[index++], done: index === len+1}
}
}
}}]);
}
add(); // [ 1, 2 ]

继续完善 将迭代器 改为生成器

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
function add(){
console.log([...{
0:1,
1:2,
2:3,
length: 3,
[Symbol.iterator]: function * (){ // 生成器函数返回的就是迭代器 它会把每次返回的结果都放在这个数组里
let index = 0;
while (index !== this.length) {
yield this[index++];
}
}
// [Symbol.iterator](){ // 迭代器
// let len = this.length;
// let index = 0;
// return {
// next:()=>{
// // 这里面谁调这个next 肯定是line13这个函数去掉它的对象next
// // 这个对象叫it 所以是it.next 所以这里用箭头函数
// // index++ 第一次的值是0 即line39 所以这里是++
// // done 这里用index和当前的len相比 如果相等了就终止了
// return {value: this[index++], done: index === len+1}
// }
// }
// }
}]);
}
add(); // [ 1, 2, 3 ]

… 会调这里的line7 Symbol.iterator方法,没这方法就不会循环

1
2
3
4
5
6
// 比如说
for(let key of {a: 1, b: 2}){

}
// 这样对象就会报错,因为它没有Symbol.iterator 方法,把这个方法加上就可以循环了
// line7-12与line13-25是一样的,建议写上面的,下面的太复杂了,每次line10 yield的时候就是调line17next方法 2个方法性质是一样的

可以在console.log里面打印[] 查看数组有Symbol.iterator 迭代器


1
2
3
4
5
6
7
8
9
10
11
12
13
function *read(){
let a = yield 1;
console.log(a);
let b = yield 2;
console.log(b);
let c = yield 3;
console.log(c);
}
let it = read();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

如图所示为执行顺序:

执行结果:

1
2
3
4
5
6
7
8
9
10
1. { value: 1, done: false }

2. undefined
{ value: 2, done: false }

3. undefined
{ value: 3, done: false }

4. undefined
{ value: undefined, done: true }

所以打印出来这里的a是undefined 要想a有值,可以如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function *read(){
let a = yield 1;
console.log(a);
let b = yield 2;
console.log(b);
let c = yield 3;
console.log(c);
}
let it = read();
console.log(it.next()); // next 第一次传参没有意义
console.log(it.next('aa')); // 这个'aa' 传给上一次yield的返回值 这里就是a
console.log(it.next('bb'));
console.log(it.next('cc'));
// 执行结果:
/*
{ value: 1, done: false }
aa
{ value: 2, done: false }
bb
{ value: 3, done: false }
cc
{ value: undefined, done: true }
*/

现在我想要第二次调用的时候报错 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function *read(){
try {
let a = yield 1;
console.log(a);
let b = yield 2;
console.log(b);
let c = yield 3;
console.log(c);
} catch (error) {
console.log('err:',error);
}

}
let it = read();
console.log(it.next());
it.throw('抛出错误');
// 执行结果:
/*
{ value: 1, done: false }
err: 抛出错误
*/

现在理解了上面的原理之后 ,现在我们来读取文件内容

示例

现在在name.txt里面放“age.txt” , age.txt文件里面放“18”

1
2
3
4
5
6
7
8
const fs = require('fs').promises;
// 希望先读name.txt 再读age.txt
function * read() {
let content = yield fs.readFile(__dirname + '/name.txt', 'utf8');
let age = yield fs.readFile(__dirname + '/' +content, 'utf8');
return age;
}
let it = read();

这里代码从哪里走到哪里 如图 这里面yield的值 是后面产生的value ,value产生的是个promise

现在只考虑then成功的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const fs = require('fs').promises;
function * read() {
let content = yield fs.readFile(__dirname + '/name.txt', 'utf8');
let age = yield fs.readFile(__dirname + '/' +content, 'utf8');
return age;
}
let it = read();

it.next().value.then(data=>{ // 这步就是上图红色框起来的部分
// data 这个data就是name.tx里面装的age.txt
//it.next(data); 这一步就是把data传给了上面的content变量;并且走了yield fs.readFile(content, 'utf8'); 这步
it.next(data).value.then(data=>{
// 这里的data就是age.txt里面存放的18 现在要把18赋值给age
let r = it.next(data);
console.log("r:",r); // r: { value: '18', done: true }
})
})

co库

安装yarn add co 可以去github上看下这个库

1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require('fs').promises;
function * read() {
let content = yield fs.readFile(__dirname + '/name.txt', 'utf8');
let age = yield fs.readFile(__dirname + '/' +content, 'utf8');
//比如新增一行 如下
let xx = yield {age:age + 10};
//return age;
return xx;
}
let co = require('co');
co(read()).then(data=>{
console.log('co:',data); // co: { age: '1810' }
})

手写co的实现原理

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
const fs = require('fs').promises;
function * read() {
let content = yield fs.readFile(__dirname + '/name.txt', 'utf8');
let age = yield fs.readFile(__dirname + '/' +content, 'utf8');
let xx = yield {age:age + 10};
return xx;
}
function co(it) {
// 因为co这里用了then所以是返回一个promise
return new Promise((resolve,reject)=>{
// 异步迭代 需要先提供一个next方法
function next(data){
let {value, done} = it.next(data);
if(!done){
// 这里的value 是指 fs.readFile(__dirname + '/name.txt', 'utf8');
// 这一串返回的,但是不一定是promise line5就不是,所以要让它变成promise
Promise.resolve(value).then(data=>{
//it.next(data);
next(data);
},err=>{
console.log(err);
})

}else{ // done是true 已完成
resolve(value);
}
}
next();
})
}
co(read()).then(data=>{
console.log('co:',data);
})

async await

1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require('fs').promises;
async function read() { // async 函数返回的是promise
let content = await fs.readFile(__dirname + '/name.txt', 'utf8');
let age = await fs.readFile(__dirname + '/' +content, 'utf8');
let xx = await {age:age + 10};
return xx;
}
read().then(data=>{
console.log("async await:", data);
},err=>{
console.log("err:", err);
})
// read()里面有错,line11都可以捕获到

还可以这样捕获错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const fs = require('fs').promises;
async function read() { // async 函数返回的是promise
try {
let content = await fs.readFile(__dirname + '/name.txt', 'utf8');
let age = await fs.readFile(__dirname + '/' +content, 'utf8');
let xx = await {age:age + 10};
return xx;
} catch (error) {
console.log("error:", error);
//return 1
}
}
read().then(data=>{
console.log("async await:", data);
//如果line4-7捕获到错误 这里返回undefined line10注释打开,这里就返回1
},err=>{
console.log("err:", err);
})

async + await 其实是generator + co的语法糖