通过js实现迭代器、可迭代对象、生成器,以及使用生成器理解async\await原理
迭代器
在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用
next()
方法实现 Iterator protocol 的任何一个对象,该方法返回具有两个属性的对象:value
,这是序列中的 next 值;和done
,如果已经迭代到序列中的最后一个值,则它为true
。如果value
和done
一起存在,则它是迭代器的返回值。
// 实现一个arr的迭代器
const arr = ['a', 'b', 'c'];
const createArrIterator = () => {
let index = 0;
return {
next() {
if (index < arr.length) {
return { done: false, value: arr[index++] };
}
return { done: true, value: undefined };
},
};
};
// test
const iterator = createArrIterator();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
可迭代对象
若一个对象拥有迭代行为,比如在
for...of
中会循环哪些值,那么那个对象便是一个可迭代对象。一些内置类型,如Array
或Map
拥有默认的迭代行为,而其他类型(比如Object
)则没有。
为了实现可迭代,一个对象必须实现 @@iterator 方法,这意味着这个对象(或其原型链中的任意一个对象)必须具有一个带Symbol.iterator
键(key)的属性。
// 实现一个可迭代对象zoo
class Zoo {
constructor(animals) {
this.animals = animals;
}
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.animals.length) {
return { value: this.animals[index++], done: false };
}
return { value: undefined, done: true };
},
// 可以监听到break return
return() {
console.log('迭代结束');
return { value: undefined, done: true };
},
};
}
}
// test
const zoo = new Zoo(['cat', 'dog', 'tiger']);
for (const item of zoo) {
if (item === 'dog') break;
console.log(item);
}
生成器函数
虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。生成器函数提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用
function*
语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator的迭代器。 通过调用生成器的下一个方法消耗值时,Generator函数将执行,直到遇到yield关键字。
// 返回值:yield value
// 传参:由上一阶段yield返回 value = yield,注意:第一段传参通过函数传
// return方法:提前结束,可以传参到value { value: 123, done: true }
// thorw方法:抛出异常,如果在上一阶段yield有捕获异常处理,后续代码将正常执行
const createGenerator = function* (param1) {
console.log(param1); // p1
const param2 = yield 10;
console.log(param2); // p2
};
const generator = createGenerator('p1');
console.log(generator.next()); // { value: 10, done: false }
console.log(generator.next('p2')); // { value: undefined, done: true }
通过生成器函数简化迭代器
yield*
表达式用于委托给另一个generator
或可迭代对象。
class Zoo {
constructor(animals) {
this.animals = animals;
}
// 自动帮你迭代可迭代对象
*[Symbol.iterator]() {
yield* this.animals;
}
}
// test
const zoo = new Zoo(['cat', 'dog', 'tiger']);
for (const item of zoo) {
console.log(item);
}
async\await原理
// 模拟两个需按序执行的接口请求
const login = function () {
return new Promise(resolve => {
setTimeout(() => {
resolve('LH1100');
}, 1500);
});
};
const getUserInfo = function (token) {
return new Promise(resolve => {
setTimeout(() => {
resolve({
name: 'lh',
token,
});
}, 1500);
});
};
function* reqGenerator() {
const token = yield login();
console.log('token', token);
const userInfo = yield getUserInfo(token);
console.log('userInfo', userInfo);
}
// 手动迭代
// const reqGen = reqGenerator();
// reqGen.next().value.then(res => {
// reqGen.next(res).value.then(res => {
// reqGen.next(res);
// });
// });
// 封装自动迭代函数
const execGen = genFn => {
const gen = genFn();
function exec(res) {
const { done, value } = gen.next(res);
if (!done) {
value.then(res => exec(res));
}
}
exec();
};
execGen(reqGenerator);