天气非常燥热,心情也异常的烦躁。真的是想要暴躁啊。
数组具有Iterator接口,其本质是有Symbol.iterator属性。
let arr = [1, 3, 5];
let iter = arr[Symbol.iterator]();
console.log(iter);
iter返回的是一个遍历器接口,有next方法。
Iterator是遍历器,for of语句的本质就是遍历器。实现Iterator接口的对象就是可遍历的,如果没有这个接口,就不能遍历。Iterator的内部实现本质是指针对象。调用next方法,移动指针指向对象中的下一个属性成员。
下面给出官方的解释:
Iterator 的遍历过程是这样的:
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
执行第一个next方法时,接口内部的指针指向数组的第一个成员 1 ,得到value值,成员 1 后面还有数组成员,所以done属性为false。再次执行next方法,指针指向 3 ,遍历未结束;接着调用next,指向 5 未结束遍历;最后一次next指针指向5的后一位,值未undefined,此时遍历结束,done值为true。
string类型也实现了Iterator接口。本来字符串就可以通过for of循环遍历出字符的。
let str = 'hello';
console.log([...str]);
Generator的作用是同Promise的作用一样,都是处理异步操作,但是Generator比Promise又好用点,结构更加清晰。Generator主要的应用场景是在请求的过程中存在业务逻辑的判断,根据判断的不同做不同的操作。
Generator的两个作用:返回Iterator遍历器对象;状态机。
在function关键字后面加上 * 就是一个Generator,yield是同Generator函数配套使用的,Generator状态机的实现就是通过yield实现的。
function* funName(){
yield 表达式,
yield 表达式,
......
}
简单的小栗子:
function* generatorFun() {
yield 123;
yield 234;
return "ending";
}
let gen = generatorFun();
console.log(gen);
定义了Generator函数generatorFun,内部有两个yield表达式,123和234,所以该函数有三个状态:123、234和return语句。
Generator函数也是函数,调用同样是使用一对() ,不过Generator函数调用之后不执行,而是返回一个Iterator遍历器对象,必须使用next方法调用下一个状态。
代码的执行遇到yield暂停,调用next方法使得遍历器内部指针对象从函数头部或者上次停止得位置开始执行,直到遇到下一个yield。简言之就是,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
使用next执行:
function* generatorFun() {
console.log("开始执行");
yield 123;
console.log("第一个yield执行完毕");
yield 234;
console.log("第二个yield执行完毕");
return "ending";
console.log("return");
}
let gen = generatorFun();
console.log(gen.next()); //{value: 123, done: false}
console.log(gen.next()); //{value: 234, done: false}
console.log(gen.next()); //{value: ending, done: true}
console.log(gen.next()); // {value:undefined,done:true}
执行第一个next时,输出 ” 开始执行 “,遇到yield停止,同时把yield后面得表达式得值存入当前成员得value中123;第二次调用next,输出文字,遇到第二个yield停止,取得表达式的值234;第三次调用next, 返回ending,函数结束。return语句后面的语句不执行。
调用next方法时传递参数,传递参数会赋值给上一个yield的处理结果。
先来个例子:
function* generator() {
let a = 2 * (yield 1);
let b = yield(a * 10);
return a + b;
};
let gen = generator();
console.log(gen.next()); // {value:1,done:false}
console.log(gen.next()); // {value:NaN,done:false}
console.log(gen.next()); // {value:NaN,done:true}
我们想要的结果是 a = 2,b = 20,但是在第二次调用next方法时,value值为NaN。这是为什么?因为 next取得的是 a * 10,上一次yield的返回值为1,存入value,但是 yield 1 整体的值返回的是undefined,a = yield 1 结果为NaN,2 undefined 为NaN,同样 undefined10也是NaN,最后return同样为NaN。
如果想要让a = 2,就得往next方法中传递参数
let gen2 = generator();
console.log(gen2.next()); // {value:1,done:false}
// next中的参数是上一个yeild表达式的返回值 设置yield 1 返回值为1
console.log(gen2.next(1)); // {value:20,done:false}
// 设置上一次yield(a*20) 返回值为 20
console.log(gen2.next(20)); // {value:22,done:true}
这样就得到理想结果了。
整个过程分为三个阶段:
阶段1:yield 1
阶段2:设置上一次yield返回值为1,赋值a,并执行 yield (a*10)
阶段3:设置上一次yield返回值为20,赋值b,返回 a+b
我画图来解释一下:
当一个Generator内部调用另一个Generator函数时,就需要用到yield*。
上面也说l,运行Generator函数会产生一个Iterator遍历器对象,
function* gen2() {
yield "gen211";
yield "gen210";
return "gen230";
}
function* generator() {
yield 11;
yield "22";
yield gen2(); // 遍历出来的是一个遍历器对象
return 33;
};
let gen = generator();
for (const value of gen) {
console.log(value); //return直接返回 不会被遍历出来
}
yield 后 放Generator函数,遍历得出的是遍历器函数。
yield* gen2(); // 遍历出来的是一个遍历器对象的值
yield* 会自动遍历的gen2函数
gen2函数有返回值,在generator中也是会接收到的,定义一个变量
function* generator() {
yield 11;
yield "22";
// yield gen2(); // 遍历出来的是一个遍历器对象
// yield* gen2(); // 遍历出来的是一个遍历器对象的值
let result = yield* gen2(); // 遍历出遍历器对象的值,并接收遍历器的return值
console.log("gen2中return值:" + result);
return 33;
};
let gen = generator();
for (const value of gen) {
console.log(value); //return直接返回 不会被遍历出来
}
async、await是Promise的语法糖,使用同步的代码,实现异步操作。
async 函数
async function 异步函数 返回的是一个Promise对象。
只有这个Promise对象的状态改变之后才能调用后续then中的回调函数。
// 1.
async function myDate() {
// 里面的异步操作完成后才会执行then方法的回调函数
return 1;
}
// 2.
let async1 = async function () {
// return 2;
throw new Error("error!!!!")
}
// 3.
let async2 = async () => {
return 3;
}
let p = myDate();
console.log(p); // 返回Promise对象
p.then(value => console.log(value)); // 返回值作为then方法中回调函数的参数
await 表达式
注意点:
简单使用:
function sleep(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
})
}
async function FiveInAsync() {
await sleep(5000);
// 休眠5秒接着输出
console.log(1111);
}
let time = FiveInAsync();
当await后面不是Promise对象时:
async function myDate() {
// await 2;
let res = await 2;
console.log(res);
return 1;
}
let p = myDate();
console.log(p);
直接返回表达式的值
使用try catch捕获错误:使用try catch捕获await可能出现的错误,如果不做处理的话,在出错的地方之后的代码是不会执行的。
async function myDate() {
try {
// 其他很多异步代码 ......
await Promise.reject("error");
} catch (error) {
console.log(error); // 捕获await后面Promise对象rejected状态的错误信息
}
let res = await Promise.resolve("right");
console.log(res);
}
let p = myDate();
正常来说,一个await成功返回后,才会执行下一步,如果第一个await返回时间较长的话,下一个await必须等待本次await返回完成才会执行,这叫串行,按照顺序执行。
串行代码:
function asyncFunByOne() {
return Promise.resolve("异步1");
}
function asyncFunByTow() {
return Promise.resolve("异步2");
}
async function asyncFun() {
// a 和 b 会先后执行
// await asyncFunByOne() 执行返回后,asyncFunByTow()才会执行
let a = await asyncFunByOne();
let b = await asyncFunByTow();
console.log(a, b);
}
let p = asyncFun();
并行的话就是多个await可以同时触发,不需要等待。
并行代码:
function asyncFunByOne() {
return Promise.resolve("异步1");
}
function asyncFunByTow() {
return Promise.resolve("异步2");
}
async function asyncFun() {
// 两个await同时执行 a1 b1同时触发
let a = asyncFunByOne();
let b = asyncFunByTow();
let a1 = await a;
let b1 = await b;
console.log(a1, b1);
}
let p = asyncFun();
其实并行的话,Promise.all方法也能实现,这个方法必须得等到所以Promise实例对象成功之后才返回。
热得我心情非常烦躁,暂时先写到这吧,看文档也就了解了这些基础知识,具体的案例实例都没写呢还。
坚持就是胜利,告诉自己不燥不燥,你可以的!!!
哈哈哈。