
迭代器的实现
function makeIterator(arr){
var iterRatorIndex = 0;
return{
next(){
return arr.length > iterRatorIndex ?
{value:arr[iterRatorIndex++],done:false}:
{value:undefined,done:true}
}
}
}
var test = makeIterator(['a','b']); //闭包:这里返回对象里面的函数,绑定了外部的作用域
console.log(test.next());//{value: "a", done: false}
console.log(test.next());//{value: "b", done: false}
console.log(test.next());//{value: undefined, done: true}
迭代器与迭代器模式
迭代器:能够迭代有序的,连续的结构,每一次抽取一位消耗数据的形式
迭代器模式:结构化的模式,迭代器是结构化模式的实现方式,从源以一次一个的方式抽取
抽取就是每次只遍历一个值
内部迭代器和外部迭代器
前端不分内外迭代器
内部迭代器:系统内部定义好的迭代规则,在进行调用的时候就可以拿到所有遍历的元素
外部迭代器:自己部署定义一个迭代器接口,每一次抽取一个数据、每一次迭代一个数据
部署迭代器接口——next方法(迭代对象)
对象并不具备迭代器接口(因为对象是无序的所以不能抽取),要想迭代就得部署一个迭代器接口
方法一
let obj = {
start:[1,3,2],
end:[7,8,9],
[Symbol.iterator](){
let arr = [...this.start,...this.end];
let iterRatorIndex = 0;
return{
next(){
return arr.length > iterRatorIndex ?
{value:arr[iterRatorIndex++],done:false}:
{value:undefined,done:true}
}
}
}
}
for(let a of obj){
console.log(a);
}// 1 3 2 7 8 9
方法二
map结构则是有序的,让map模拟对象进行迭代,以键值的方式进行输出
可迭代的数据类型:array/map/set/string/typeArray/NodeList/arguments
let obj = {
a:1,
b:2,
[Symbol.iterator]() {
let map = new Map();
for (let [key, value] of Object.entries(obj)) {
map.set(key, value);
}
let mapArr = [...map.entries()];
let iterRatorIndex = 0;
return {
next() {
return mapArr.length > iterRatorIndex ?
{ value: mapArr[iterRatorIndex++],done:false}:
{value:undefined,done:true}
}
}
}
}
for(let a of obj){
console.log(a);
}
//(2) ["a", 1] ["b", 2]
//抽取
var a = obj[Symbol.iterator]();
console.log(a.next());
//{value: Array(2), done: false}
用for(let [key,value] of Object.entries(obj))也能解决这个问题,但是不能实现抽取,因为不具备迭代器接口
默认会调用iterator的情况
…拓展运算符for…ofArray.from()map/setPromise.all([…])/Promise.race([…])yield部署迭代器接口——return方法(了解)
终止for循环的方法都会触发return方法(比如break/throw new Error())
let obj = {
a:1,
b:2,
[Symbol.iterator]() {
let map = new Map();
for (let [key, value] of Object.entries(obj)) {
map.set(key, value);
}
let mapArr = [...map.entries()];
let iterRatorIndex = 0;
return {
next() {
return mapArr.length > iterRatorIndex ?
{ value: mapArr[iterRatorIndex++],done:false}:
{value:undefined,done:true}
},
return() {
console.log('触发了错误');
return {value: 1, done: false} // 不返回该对象会报错
}
}
}
}
for(var i of obj){
console.log(i);
throw new Error('错误');
}
//["a", 1]
//触发了错误
//Uncaught Error: 错误
二、generator
概念
生成器函数,返回值是迭代器实例
基本形式
function * test(){}
let iter = test();
console.log(iter);
test {<suspended>}
//[[GeneratorLocation]]: VM657:1
//[[Prototype]]: Generator
//[[Prototype]]: Generator
//constructor: GeneratorFunction {prototype: Generator, Symbol(Symbol.toStringTag): 'GeneratorFunction', constructor: ƒ}
//next: ƒ next() <<
//return: ƒ return()
//throw: ƒ throw()
//Symbol(Symbol.toStringTag): "Generator"
...
yield
特点
产出迭代器对象,暂停函数运行
返回值默认是undefined,如果要有返回值则需通过next传参
function * test(){
console.log(1);
yield 'a';
console.log(2);
yield 'b';
console.log(3);
let a = yield '5'
console.log(a);
return 'd';
yield 'c';
}
let iter = test();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
// 1
// {value: "a", done: false}
// 2
// {value: "b", done: false}
// 3
// undefined
// {value: "d", done: true}
// {value: undefined, done: true}
yield是一个单独的表达式,嵌套使用要加()
function * test(){
console.log('hello'+(yield 123));
}
let iter = test();
console.log(iter.next());
{value: 123, done: false}
yield作为函数形参
function * test(){
foo(yield 'a',yield 'c')
}
function foo(a,b){
console.log(a,b);
}
let iter = test();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
// {value: "a", done: false}
// {value: "c", done: false}
// undefined undefined
// {value: undefined, done: true}
yield/return区别:
yield 暂停(有记忆的功能)
return 结束(没有记忆功能)
注: yield 只能出现在生成器函数中,否则会报错
for…of 遍历产出值
function * test(){
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}
for(let i of test()){
console.log(i);
}
//1 2 3 4 5
通过next给yield传参
永远拿不到第一次的值,所以第一个参数没必要传,蛇形传值
function * test(){
let value1 = yield 1; // 蛇形传值方式
console.log('value1'+value1);
let value2 = yield 2;
console.log('value2'+value2);
let value3 = yield 3;
console.log('value3'+value3);
let value4 = yield 4;
console.log('value4'+value4);
}
let iter = test();
console.log(iter.next('none'));
console.log(iter.next('two'));
console.log(iter.next('three'));
// {value: 1, done: false}
// value1two
// {value: 2, done: false}
// value2three
// {value: 3, done: false}
迭代器接口优化
因为yield得产出值就为迭代函数,所以可以直接用yield去替代以前next的写法
let obj = {
start:[1,2,3],
end:[4,5,6],
[Symbol.iterator] : function*(){
var itemIndx = [...this.start,...this.end],
length = itemIndx.length,
idxof = 0;
while(length>idxof){
yield itemIndx[idxof++]
}
}
}
for(var c of obj){
console.log(c);
}
console.log(obj[Symbol.iterator]().next());
// 1 2 3 4 5 6 {value: 1, done: false}
迭代器方法
next() —— 抽取产出值,返回值是迭代器对象
return() —— 终止迭代,往后迭代都是 {value:undefine,done:true}
throw() —— 抛出错误,由于yield会暂停,导致try-catch没执行完,所以捕获不到,只有try-catch执行完了才能捕获到错误(相当于next,也可以抽取迭代)
var g = function * (){
yield;
try{
yield;
}catch(e){
console.log('生成器内部异常:' + e);
}
console.log(1);
}
var i = g();
console.log(i.next()); // {value:undefined,done:false} --没执行完
console.log(i.next()); // {value:undefined,done:false} --执行完了
console.log(i.throw('a')); // 生成器内部异常:a --抛出错误 注意:这里只能throw一次,两次会报错
console.log(i.next()); // {value:undefined,done:true}
cry/catch可以捕获生成函数的异步错误
let fs = require('fs');
let util = require('util');
let co = require('co');
let readFile = util.promisify(fs.readFile);
function * read(){
let value1 = yield readFile('./name.txt', 'utf-8');
let value2 = yield readFile(value1, 'utf-8');
let value3 = yield readFile(value2, 'utf-8');
return value3;
}
let promise = co(read());
promise.then((val)=>{
console.log(val);
});
// 99
generator状态机
generator状态机
let fs = require('fs')
function promisify(fn){
return function(...args){
return new Promise((resolve,reject)=>{
fn(...args,(err,data)=>{
if(data){
resolve(data)
}else{
reject(err)
}
})
})
}
}
let readFile = promisify(fs.readFile)
function * read(){
let value1 = yield readFile('./name.txt','utf-8');
let value2 = yield readFile(value1,'utf-8');
let value3 = yield readFile(value2,'utf-8');
console.log(value3);
}
let iter = read();
let {value,done} = iter.next();
value.then((val)=>{
let{value,done} = iter.next(val)
value.then((val2)=>{
let{value,done} = iter.next(val2)
value.then((value)=>{
console.log(value);
})
})
})
// 99
步骤:
首先通过Promisify把异步函数promise化
然后把所有异步函数作为产出值放入生成函数,通过next进行抽取调用,返回值是迭代器对象{value:promise,done:false}
再通过模式匹配拿到promise,通过then拿到数据,继续通过next进行抽取调用
co源码实现
co是一个模块,是由TJ开发(整个git最高效、高产的一个人)
function Co(iter){
return new Promise((resolve,reject)=>{
let next = (data) =>{
let {value,done} = iter.next(data);
if(done){
resolve(value)
}else{
value.then((val)=>{
next(val);
})
}
}
next(); // 递归
})
}
let promise = Co(read())
promise.then((val)=>{
console.log(val);
})
// 99
co
它让异步的执行更加简单,不需要每次都调用next
它也是async的由来
npm i co -D
let co = require('co');
let promise = Co(read())
promise.then((val)=>{
console.log(val);
});
// 99
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)